<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <title>Eetal&#39;s Blog</title>

  <!-- keywords -->
  

  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="The eetal&apos;s hexo blog in gitee">
<meta property="og:type" content="website">
<meta property="og:title" content="Eetal&#39;s Blog">
<meta property="og:url" content="http://eetal.gitee.io/blog/index.html">
<meta property="og:site_name" content="Eetal&#39;s Blog">
<meta property="og:description" content="The eetal&apos;s hexo blog in gitee">
<meta property="og:locale" content="default">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Eetal&#39;s Blog">
<meta name="twitter:description" content="The eetal&apos;s hexo blog in gitee">
  
    <link rel="alternative" href="/atom.xml" title="Eetal&#39;s Blog" type="application/atom+xml">
  
  
    <link rel="icon" href="http://www.yangyitao.top/FiveChess/resource/ico/favicon.ico">
  
  <link rel="stylesheet" href="/blog/css/style.css">
  
  

  <script src="//cdn.bootcss.com/require.js/2.3.2/require.min.js"></script>
  <script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>

  
</head>
<body>
  <div id="container">
    <div id="particles-js"></div>
    <div class="left-col">
    <div class="overlay"></div>
<div class="intrude-less">
	<header id="header" class="inner">
		<a href="/" class="profilepic">
			
			<img lazy-src="/blog/images/head.jpg" class="js-avatar">
			
		</a>

		<hgroup>
		  <h1 class="header-author"><a href="/">Eetal</a></h1>
		</hgroup>

		
		<p class="header-subtitle">java coder</p>
		

		
			<div class="switch-btn">
				<div class="icon">
					<div class="icon-ctn">
						<div class="icon-wrap icon-house" data-idx="0">
							<div class="birdhouse"></div>
							<div class="birdhouse_holes"></div>
						</div>
						<div class="icon-wrap icon-ribbon hide" data-idx="1">
							<div class="ribbon"></div>
						</div>
						
						<div class="icon-wrap icon-link hide" data-idx="2">
							<div class="loopback_l"></div>
							<div class="loopback_r"></div>
						</div>
						
						
					</div>
					
				</div>
				<div class="tips-box hide">
					<div class="tips-arrow"></div>
					<ul class="tips-inner">
						<li>菜单</li>
						<li>标签</li>
						
						<li>友情链接</li>
						
						
					</ul>
				</div>
			</div>
		

		<div class="switch-area">
			<div class="switch-wrap">
				<section class="switch-part switch-part1">
					<nav class="header-menu">
						<ul>
						
							<li><a href="/blog/">Home</a></li>
				        
							<li><a href="/blog/archives">Archives</a></li>
				        
						</ul>
					</nav>
					<nav class="header-nav">
						<div class="social">
							
						</div>
					</nav>
				</section>
				
				
				<section class="switch-part switch-part2">
					<div class="widget tagcloud" id="js-tagcloud">
						
					</div>
				</section>
				
				
				
				<section class="switch-part switch-part3">
					<div id="js-friends">
					
			          <a target="_blank" class="main-nav-link switch-friends-link" href="https://github.com/smackgg/hexo-theme-smackdown">smackdown</a>
			        
			        </div>
				</section>
				

				
			</div>
		</div>
	</header>				
</div>
    </div>
    <div class="mid-col">
      <nav id="mobile-nav">
  	<div class="overlay">
  		<div class="slider-trigger"></div>
  		<h1 class="header-author js-mobile-header hide">Eetal</h1>
  	</div>
	<div class="intrude-less">
		<header id="header" class="inner">
			<div class="profilepic">
				<img lazy-src="/blog/images/head.jpg" class="js-avatar">
			</div>
			<hgroup>
			  <h1 class="header-author">Eetal</h1>
			</hgroup>
			
			<p class="header-subtitle">java coder</p>
			
			<nav class="header-menu">
				<ul>
				
					<li><a href="/blog/">Home</a></li>
		        
					<li><a href="/blog/archives">Archives</a></li>
		        
		        <div class="clearfix"></div>
				</ul>
			</nav>
			<nav class="header-nav">
				<div class="social">
					
				</div>
			</nav>
		</header>				
	</div>
</nav>
      <div class="body-wrap">
  
    <article id="post-基于zookeeper实现分布式锁" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/08/01/基于zookeeper实现分布式锁/" class="article-date">
  	<time datetime="2019-08-01T08:01:55.356Z" itemprop="datePublished">2019-08-01</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/08/01/基于zookeeper实现分布式锁/">
        基于zookeeper实现分布式锁
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第二十七篇博客–基于zookeeper实现分布式锁</p>
<h2 id="分布式锁设计"><a href="#分布式锁设计" class="headerlink" title="分布式锁设计"></a>分布式锁设计</h2><p>zookeeper的临时节点在会话断开后，则会自动删除，这个特性使其可以非常好的应用在分布式锁<br>而如果直接使用某一个path的临时节点作为分布式锁，会导致”惊群效应“（当锁每次释放都会唤醒全部竞争节点，而实际只能有一个竞争到）<br>所以结合有序节点进行优化，对于某一个特定的节点RootNode创建临时有序子节点<br>当一个客户端要获取锁时，就会新增一个临时有序子节点<br>在尝试获取锁时进行判断，查看RootNode当前的所有子节点信息，判断自己是否是最小的子节点（因为zookeeper的有序节点会从1递增）<br>如果是最小的子节点，则获得锁，在释放锁时，删除自己创建的这个临时有序节点<br>如果不是最小节点，代表还有别人在占用锁，则注册一个事件监听子节点中节点序号刚好小于自己的节点的删除事件，当监听到事件时，则代表获取锁成功<br>因为是临时节点，保证了出现某一台客户端占据锁但是断开连接仍然会删除其创建的临时节点<br>通过这个设计避免了”惊群效应“，某一个锁的释放只会触发刚好晚于他创建临时有序节点的的下一个等待锁的客户端的事件，由其获得锁</p>
<h2 id="demo源码"><a href="#demo源码" class="headerlink" title="demo源码"></a>demo源码</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 基于zk实现分布式锁</span><br><span class="line"> */</span><br><span class="line">public class DistributedLock implements Watcher &#123;</span><br><span class="line">    String ROOT_LOCK;//作为锁的命名空间标识</span><br><span class="line">    String WAIT_LOCK;//正在等待的锁的路径</span><br><span class="line">    String CURRENT_LOCK;//当前请求对应的锁</span><br><span class="line">    ZooKeeper zk;</span><br><span class="line">    CountDownLatch countDownLatch;</span><br><span class="line"></span><br><span class="line">    public DistributedLock(ZooKeeper zk,String ROOT_LOCK)throws Exception&#123;</span><br><span class="line">            this.zk = zk;</span><br><span class="line">            this.ROOT_LOCK = ROOT_LOCK;</span><br><span class="line">            <span class="keyword">if</span>(zk.exists(ROOT_LOCK,<span class="literal">false</span>) == null)&#123;</span><br><span class="line">                throw new Exception(<span class="string">"RootLock node not exists!"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public boolean <span class="function"><span class="title">tryLock</span></span>() &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            CURRENT_LOCK = zk.create(ROOT_LOCK+<span class="string">"/"</span>,<span class="string">"0"</span>.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);</span><br><span class="line">            List&lt;String&gt; children = zk.getChildren(ROOT_LOCK,<span class="literal">false</span>);</span><br><span class="line">            SortedSet&lt;String&gt; sortChildren = new TreeSet&lt;String&gt;();</span><br><span class="line">            sortChildren.addAll(children);</span><br><span class="line">            //获得排序小于CURRENT_LOCK的childNode的path</span><br><span class="line">            SortedSet&lt;String&gt; lessThanMe = sortChildren.headSet(CURRENT_LOCK);</span><br><span class="line">            <span class="keyword">if</span>(lessThanMe.isEmpty())</span><br><span class="line">                <span class="built_in">return</span> <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">else</span>&#123;</span><br><span class="line">				//存在小于自己的节点，获取失败</span><br><span class="line">                WAIT_LOCK = lessThanMe.last();</span><br><span class="line">                <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; catch (Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void <span class="function"><span class="title">lock</span></span>() &#123;</span><br><span class="line">        <span class="keyword">while</span>(!tryLock())&#123;</span><br><span class="line">			//出现异常则继续tryLock</span><br><span class="line">            try &#123;</span><br><span class="line">                <span class="keyword">if</span>(zk.exists(WAIT_LOCK,this)!=null)&#123;</span><br><span class="line">                    countDownLatch = new CountDownLatch(1);</span><br><span class="line">                    countDownLatch.await();</span><br><span class="line">                    //获得锁，结束</span><br><span class="line">                    <span class="built_in">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; catch (Exception e)&#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void <span class="function"><span class="title">unlock</span></span>() &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            zk.delete(CURRENT_LOCK,-1);</span><br><span class="line">        &#125; catch (Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void process(WatchedEvent event) &#123;</span><br><span class="line">        <span class="keyword">if</span>(event.getType() == Event.EventType.NodeDeleted)</span><br><span class="line">            countDownLatch.countDown();</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>第三方工具也有提供基于zookeeper实现的更多分布式锁的设计，如curator的recipes工程</p>
<h3 id="更多精彩内容"><a href="#更多精彩内容" class="headerlink" title="更多精彩内容"></a>更多精彩内容</h3><h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-zookeeper源码分析-事件注册及通信原理" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/07/31/zookeeper源码分析-事件注册及通信原理/" class="article-date">
  	<time datetime="2019-07-31T12:24:19.197Z" itemprop="datePublished">2019-07-31</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/07/31/zookeeper源码分析-事件注册及通信原理/">
        zookeeper源码分析-事件注册及通信原理
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第二十六篇博客–zookeeper源码分析-事件注册及通信原理</p>
<h2 id="zookeeper基本命令代码"><a href="#zookeeper基本命令代码" class="headerlink" title="zookeeper基本命令代码"></a>zookeeper基本命令代码</h2><h3 id="依赖"><a href="#依赖" class="headerlink" title="依赖"></a>依赖</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;groupId&gt;org.apache.zookeeper&lt;/groupId&gt;</span><br><span class="line">&lt;artifactId&gt;zookeeper&lt;/artifactId&gt;</span><br><span class="line">&lt;version&gt;3.5.5&lt;/version&gt;</span><br></pre></td></tr></table></figure>
<h3 id="demo代码"><a href="#demo代码" class="headerlink" title="demo代码"></a>demo代码</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">final CountDownLatch coutnDownLatch = new CountDownLatch(1);</span><br><span class="line">ZooKeeper zookeeper = new ZooKeeper(<span class="string">"192.168.156.171:2181,192.168.156.171:2182,192.168.156.171:2183"</span>,</span><br><span class="line">						3000,</span><br><span class="line">						new <span class="function"><span class="title">Watcher</span></span>() &#123;</span><br><span class="line"></span><br><span class="line">							public void process(WatchedEvent event) &#123;</span><br><span class="line">								//注册默认的watcher，当连接上时，会触发该watcher</span><br><span class="line">								//对该zookeeper实例的节点进行操作，watcher填<span class="literal">true</span>使用此默认watcher</span><br><span class="line">								System.out.println(<span class="string">"defaultWatcher process eventType : "</span>+event.getPath()+<span class="string">"-"</span>+event.getType());</span><br><span class="line">								coutnDownLatch.countDown();</span><br><span class="line">							&#125;</span><br><span class="line">	</span><br><span class="line">&#125;);</span><br><span class="line">System.out.println(zookeeper.getState());//connecting---连接中</span><br><span class="line">coutnDownLatch.await();</span><br><span class="line">System.out.println(zookeeper.getState());//connected---已连接</span><br><span class="line">//createMode 模式，PERSISTENT---持久化节点</span><br><span class="line">Stat <span class="built_in">stat</span> = new Stat();</span><br><span class="line">/**</span><br><span class="line"> * 节点状态，包含version</span><br><span class="line"> * create、delete、setData语句会更新节点<span class="built_in">stat</span></span><br><span class="line"> * 并且create、delete子节点会更新父节点的<span class="built_in">stat</span></span><br><span class="line"> * 在create、getData语句传入<span class="built_in">stat</span>，执行后会把最新的<span class="built_in">stat</span>属性复制到传入的<span class="built_in">stat</span>对象中</span><br><span class="line"> * setData、delete语句传入<span class="built_in">stat</span>的version，通过乐观锁验证版本号保证数据同步，版本不对时抛出BadVersionException（如果version是-1，代表不进行版本验证）</span><br><span class="line"> * setData、exists语句返回值为最新的<span class="built_in">stat</span></span><br><span class="line"> */</span><br><span class="line">zookeeper.delete(<span class="string">"/java"</span>,-1);</span><br><span class="line">String path = zookeeper.create(<span class="string">"/java"</span>, <span class="string">"2019-07-29"</span>.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,<span class="built_in">stat</span>);//返回路径</span><br><span class="line">System.out.println(<span class="string">"path : "</span>+path);</span><br><span class="line">byte[] byteV = zookeeper.getData(path, null, <span class="built_in">stat</span>);</span><br><span class="line">System.out.println(<span class="string">"version:"</span>+stat.getVersion()+<span class="string">"-"</span>+new String(byteV));</span><br><span class="line">/**</span><br><span class="line"> * </span><br><span class="line"> * OPEN_ACL_UNSAFE---完全开放</span><br><span class="line"> * CREATOR_ALL_ACL---创建者拥有权限</span><br><span class="line"> * READ_ACL_UNSAFE---开放读权限</span><br><span class="line"> * ANYONE_ID_UNSAFE---有验证的用户就不被全部权限</span><br><span class="line"> * AUTH_IDS---只对指定用户开放</span><br><span class="line"> */</span><br><span class="line"><span class="built_in">stat</span> = zookeeper.setData(path, <span class="string">"2019-07-30"</span>.getBytes(), stat.getVersion());</span><br><span class="line">zookeeper.getData(path, <span class="literal">true</span>, <span class="built_in">stat</span>);//<span class="literal">true</span>代表注册watcher，使用默认watcher即创建Zookeeper实例时注册的watcher，只响应一次事件</span><br><span class="line">zookeeper.getData(path, new <span class="function"><span class="title">Watcher</span></span>() &#123;//注册专用的watcher，只响应一次事件</span><br><span class="line">	public void process(WatchedEvent event) &#123;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;, <span class="built_in">stat</span>);</span><br><span class="line"></span><br><span class="line">zookeeper.setData(path, <span class="string">"2019-07-30"</span>.getBytes(), stat.getVersion());</span><br><span class="line">byteV = zookeeper.getData(path, null, <span class="built_in">stat</span>);</span><br><span class="line">System.out.println(<span class="string">"version:"</span>+stat.getVersion()+<span class="string">"-"</span>+new String(byteV));</span><br></pre></td></tr></table></figure>
<h2 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h2><p>Zookeeper对于集群信息提供jmx的监控支持，详情欢迎查看本人另一篇博客——<a href="https://blog.csdn.net/weixin_44627989/article/details/97636574" target="_blank" rel="noopener">zookeeper源码分析-选举算法</a><br>而对于客户端连接使用的是netty做socket的io处理，实现类为ClientCnxnSocketNetty，还有另一个可选的nio实现类是ClientCnxnSocketNIO<br>Zookeeper构造函数,实例化最终会到下面的构造函数，根据默认配置，创建一个ZKWatchManager对象，将默认wathcer注入该wathcerManager的defaultWatcher成员<br>将该watcherManager以及包含了集群信息的zookeeper对象传递用于实例化一个ClientCnxn对象，并启动<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">protected ZKWatchManager <span class="function"><span class="title">defaultWatchManager</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> new ZKWatchManager(getClientConfig().getBoolean(ZKClientConfig.DISABLE_AUTO_WATCH_RESET));</span><br><span class="line">&#125;</span><br><span class="line">public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,</span><br><span class="line">        boolean canBeReadOnly, HostProvider aHostProvider,</span><br><span class="line">        ZKClientConfig clientConfig) throws IOException &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (clientConfig == null) &#123;</span><br><span class="line">        clientConfig = new ZKClientConfig();</span><br><span class="line">    &#125;</span><br><span class="line">    this.clientConfig = clientConfig;</span><br><span class="line">    watchManager = defaultWatchManager();</span><br><span class="line">    watchManager.defaultWatcher = watcher;</span><br><span class="line">    ConnectStringParser connectStringParser = new ConnectStringParser(</span><br><span class="line">            connectString);</span><br><span class="line">    hostProvider = aHostProvider;</span><br><span class="line"></span><br><span class="line">    cnxn = createConnection(connectStringParser.getChrootPath(),</span><br><span class="line">            hostProvider, sessionTimeout, this, watchManager,</span><br><span class="line">            getClientCnxnSocket(), canBeReadOnly);</span><br><span class="line">    cnxn.start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// @VisibleForTesting</span><br><span class="line">protected ClientCnxn createConnection(String chrootPath,</span><br><span class="line">        HostProvider hostProvider, int sessionTimeout, ZooKeeper zooKeeper,</span><br><span class="line">        ClientWatchManager watcher, ClientCnxnSocket clientCnxnSocket,</span><br><span class="line">        boolean canBeReadOnly) throws IOException &#123;</span><br><span class="line">    <span class="built_in">return</span> new ClientCnxn(chrootPath, hostProvider, sessionTimeout, this,</span><br><span class="line">            watchManager, clientCnxnSocket, canBeReadOnly);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>ClientCnxn在实例化时会实例化两个线程成员，作为发送消息的线程以及处理事件的线程<br>因为zookeeper内部使用netty所以发送消息的响应式异步的，当netty收到响应时，将响应封装为一个event，数据的封装返回在evetThread处理<br>要发送的包由一个阻塞队列保存，已发送待确认的包由一个链表保存<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">private final LinkedList&lt;Packet&gt; pendingQueue = new LinkedList&lt;Packet&gt;();//等待响应的队列</span><br><span class="line"></span><br><span class="line">private final LinkedBlockingDeque&lt;Packet&gt; outgoingQueue = new LinkedBlockingDeque&lt;Packet&gt;();//要发送的队列</span><br><span class="line"></span><br><span class="line">public ClientCnxn(String chrootPath, HostProvider hostProvider, int sessionTimeout, ZooKeeper zooKeeper,</span><br><span class="line">        ClientWatchManager watcher, ClientCnxnSocket clientCnxnSocket,</span><br><span class="line">        long sessionId, byte[] sessionPasswd, boolean canBeReadOnly) &#123;</span><br><span class="line">    this.zooKeeper = zooKeeper;</span><br><span class="line">    this.watcher = watcher;</span><br><span class="line">    this.sessionId = sessionId;</span><br><span class="line">    this.sessionPasswd = sessionPasswd;</span><br><span class="line">    this.sessionTimeout = sessionTimeout;</span><br><span class="line">    this.hostProvider = hostProvider;</span><br><span class="line">    this.chrootPath = chrootPath;</span><br><span class="line"></span><br><span class="line">    connectTimeout = sessionTimeout / hostProvider.size();</span><br><span class="line">    readTimeout = sessionTimeout * 2 / 3;</span><br><span class="line">    readOnly = canBeReadOnly;</span><br><span class="line"></span><br><span class="line">    sendThread = new SendThread(clientCnxnSocket);</span><br><span class="line">    eventThread = new EventThread();</span><br><span class="line">    this.clientConfig=zooKeeper.getClientConfig();</span><br><span class="line">    initRequestTimeout();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public void <span class="function"><span class="title">start</span></span>() &#123;</span><br><span class="line">    sendThread.start();</span><br><span class="line">    eventThread.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>sendThread的处理逻辑为，当服务器状态为活跃时，循环检查计算心跳间隔时间，添加pin包到待发送队列，后面发送以保持连接<br>每一轮最后调用clientCnxnSocket.doTransport(to, pendingQueue, ClientCnxn.this)去开始传输<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br></pre></td><td class="code"><pre><span class="line">public void <span class="function"><span class="title">run</span></span>() &#123;</span><br><span class="line">           clientCnxnSocket.introduce(this, sessionId, outgoingQueue);</span><br><span class="line">           clientCnxnSocket.updateNow();</span><br><span class="line">           clientCnxnSocket.updateLastSendAndHeard();</span><br><span class="line">           int to;</span><br><span class="line">           long lastPingRwServer = Time.currentElapsedTime();</span><br><span class="line">           final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds</span><br><span class="line">           InetSocketAddress serverAddress = null;</span><br><span class="line">           <span class="keyword">while</span> (state.isAlive()) &#123;</span><br><span class="line">               try &#123;</span><br><span class="line">                   <span class="keyword">if</span> (!clientCnxnSocket.isConnected()) &#123;</span><br><span class="line">                       // don<span class="string">'t re-establish connection if we are closing</span></span><br><span class="line"><span class="string">                       if (closing) &#123;</span></span><br><span class="line"><span class="string">                           break;</span></span><br><span class="line"><span class="string">                       &#125;</span></span><br><span class="line"><span class="string">                       if (rwServerAddress != null) &#123;</span></span><br><span class="line"><span class="string">                           serverAddress = rwServerAddress;</span></span><br><span class="line"><span class="string">                           rwServerAddress = null;</span></span><br><span class="line"><span class="string">                       &#125; else &#123;</span></span><br><span class="line"><span class="string">                           serverAddress = hostProvider.next(1000);</span></span><br><span class="line"><span class="string">                       &#125;</span></span><br><span class="line"><span class="string">                       startConnect(serverAddress);</span></span><br><span class="line"><span class="string">                       clientCnxnSocket.updateLastSendAndHeard();</span></span><br><span class="line"><span class="string">                   &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                   if (state.isConnected()) &#123;</span></span><br><span class="line"><span class="string">                       // determine whether we need to send an AuthFailed event.</span></span><br><span class="line"><span class="string">                       if (zooKeeperSaslClient != null) &#123;</span></span><br><span class="line"><span class="string">                           boolean sendAuthEvent = false;</span></span><br><span class="line"><span class="string">                           if (zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.INITIAL) &#123;</span></span><br><span class="line"><span class="string">                               try &#123;</span></span><br><span class="line"><span class="string">                                   zooKeeperSaslClient.initialize(ClientCnxn.this);</span></span><br><span class="line"><span class="string">                               &#125; catch (SaslException e) &#123;</span></span><br><span class="line"><span class="string">                                  LOG.error("SASL authentication with Zookeeper Quorum member failed: " + e);</span></span><br><span class="line"><span class="string">                                   state = States.AUTH_FAILED;</span></span><br><span class="line"><span class="string">                                   sendAuthEvent = true;</span></span><br><span class="line"><span class="string">                               &#125;</span></span><br><span class="line"><span class="string">                           &#125;</span></span><br><span class="line"><span class="string">                           KeeperState authState = zooKeeperSaslClient.getKeeperState();</span></span><br><span class="line"><span class="string">                           if (authState != null) &#123;</span></span><br><span class="line"><span class="string">                               if (authState == KeeperState.AuthFailed) &#123;</span></span><br><span class="line"><span class="string">                                   // An authentication error occurred during authentication with the Zookeeper Server.</span></span><br><span class="line"><span class="string">                                   state = States.AUTH_FAILED;</span></span><br><span class="line"><span class="string">                                   sendAuthEvent = true;</span></span><br><span class="line"><span class="string">                               &#125; else &#123;</span></span><br><span class="line"><span class="string">                                   if (authState == KeeperState.SaslAuthenticated) &#123;</span></span><br><span class="line"><span class="string">                                       sendAuthEvent = true;</span></span><br><span class="line"><span class="string">                                   &#125;</span></span><br><span class="line"><span class="string">                               &#125;</span></span><br><span class="line"><span class="string">                           &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                           if (sendAuthEvent) &#123;</span></span><br><span class="line"><span class="string">                               eventThread.queueEvent(new WatchedEvent(</span></span><br><span class="line"><span class="string">                                     Watcher.Event.EventType.None,</span></span><br><span class="line"><span class="string">                                     authState,null));</span></span><br><span class="line"><span class="string">                               if (state == States.AUTH_FAILED) &#123;</span></span><br><span class="line"><span class="string">                                 eventThread.queueEventOfDeath();</span></span><br><span class="line"><span class="string">                               &#125;</span></span><br><span class="line"><span class="string">                           &#125;</span></span><br><span class="line"><span class="string">                       &#125;</span></span><br><span class="line"><span class="string">                       to = readTimeout - clientCnxnSocket.getIdleRecv();</span></span><br><span class="line"><span class="string">                   &#125; else &#123;</span></span><br><span class="line"><span class="string">                       to = connectTimeout - clientCnxnSocket.getIdleRecv();</span></span><br><span class="line"><span class="string">                   &#125;</span></span><br><span class="line"><span class="string">                   </span></span><br><span class="line"><span class="string">                   if (to &lt;= 0) &#123;</span></span><br><span class="line"><span class="string">                       String warnInfo;</span></span><br><span class="line"><span class="string">                       warnInfo = "Client session timed out, have not heard from server in "</span></span><br><span class="line"><span class="string">                           + clientCnxnSocket.getIdleRecv()</span></span><br><span class="line"><span class="string">                           + "ms"</span></span><br><span class="line"><span class="string">                           + " for sessionid 0x"</span></span><br><span class="line"><span class="string">                           + Long.toHexString(sessionId);</span></span><br><span class="line"><span class="string">                       LOG.warn(warnInfo);</span></span><br><span class="line"><span class="string">                       throw new SessionTimeoutException(warnInfo);</span></span><br><span class="line"><span class="string">                   &#125;</span></span><br><span class="line"><span class="string">                   if (state.isConnected()) &#123;</span></span><br><span class="line"><span class="string">                   	//1000(1 second) is to prevent race condition missing to send the second ping</span></span><br><span class="line"><span class="string">                   	//also make sure not to send too many pings when readTimeout is small </span></span><br><span class="line"><span class="string">                       int timeToNextPing = readTimeout / 2 - clientCnxnSocket.getIdleSend() - </span></span><br><span class="line"><span class="string">                       		((clientCnxnSocket.getIdleSend() &gt; 1000) ? 1000 : 0);</span></span><br><span class="line"><span class="string">                       //send a ping request either time is due or no packet sent out within MAX_SEND_PING_INTERVAL</span></span><br><span class="line"><span class="string">                       if (timeToNextPing &lt;= 0 || clientCnxnSocket.getIdleSend() &gt; MAX_SEND_PING_INTERVAL) &#123;</span></span><br><span class="line"><span class="string">                           sendPing();</span></span><br><span class="line"><span class="string">                           clientCnxnSocket.updateLastSend();</span></span><br><span class="line"><span class="string">                       &#125; else &#123;</span></span><br><span class="line"><span class="string">                           if (timeToNextPing &lt; to) &#123;</span></span><br><span class="line"><span class="string">                               to = timeToNextPing;</span></span><br><span class="line"><span class="string">                           &#125;</span></span><br><span class="line"><span class="string">                       &#125;</span></span><br><span class="line"><span class="string">                   &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                   // If we are in read-only mode, seek for read/write server</span></span><br><span class="line"><span class="string">                   if (state == States.CONNECTEDREADONLY) &#123;</span></span><br><span class="line"><span class="string">                       long now = Time.currentElapsedTime();</span></span><br><span class="line"><span class="string">                       int idlePingRwServer = (int) (now - lastPingRwServer);</span></span><br><span class="line"><span class="string">                       if (idlePingRwServer &gt;= pingRwTimeout) &#123;</span></span><br><span class="line"><span class="string">                           lastPingRwServer = now;</span></span><br><span class="line"><span class="string">                           idlePingRwServer = 0;</span></span><br><span class="line"><span class="string">                           pingRwTimeout =</span></span><br><span class="line"><span class="string">                               Math.min(2*pingRwTimeout, maxPingRwTimeout);</span></span><br><span class="line"><span class="string">                           pingRwServer();</span></span><br><span class="line"><span class="string">                       &#125;</span></span><br><span class="line"><span class="string">                       to = Math.min(to, pingRwTimeout - idlePingRwServer);</span></span><br><span class="line"><span class="string">                   &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                   clientCnxnSocket.doTransport(to, pendingQueue, ClientCnxn.this);</span></span><br><span class="line"><span class="string">               &#125; catch (Throwable e) &#123;</span></span><br><span class="line"><span class="string">                   if (closing) &#123;</span></span><br><span class="line"><span class="string">                       if (LOG.isDebugEnabled()) &#123;</span></span><br><span class="line"><span class="string">                           // closing so this is expected</span></span><br><span class="line"><span class="string">                           LOG.debug("An exception was thrown while closing send thread for session 0x"</span></span><br><span class="line"><span class="string">                                   + Long.toHexString(getSessionId())</span></span><br><span class="line"><span class="string">                                   + " : " + e.getMessage());</span></span><br><span class="line"><span class="string">                       &#125;</span></span><br><span class="line"><span class="string">                       break;</span></span><br><span class="line"><span class="string">                   &#125; else &#123;</span></span><br><span class="line"><span class="string">                       // this is ugly, you have a better way speak up</span></span><br><span class="line"><span class="string">                       if (e instanceof SessionExpiredException) &#123;</span></span><br><span class="line"><span class="string">                           LOG.info(e.getMessage() + ", closing socket connection");</span></span><br><span class="line"><span class="string">                       &#125; else if (e instanceof SessionTimeoutException) &#123;</span></span><br><span class="line"><span class="string">                           LOG.info(e.getMessage() + RETRY_CONN_MSG);</span></span><br><span class="line"><span class="string">                       &#125; else if (e instanceof EndOfStreamException) &#123;</span></span><br><span class="line"><span class="string">                           LOG.info(e.getMessage() + RETRY_CONN_MSG);</span></span><br><span class="line"><span class="string">                       &#125; else if (e instanceof RWServerFoundException) &#123;</span></span><br><span class="line"><span class="string">                           LOG.info(e.getMessage());</span></span><br><span class="line"><span class="string">                       &#125; else if (e instanceof SocketException) &#123;</span></span><br><span class="line"><span class="string">                           LOG.info("Socket error occurred: &#123;&#125;: &#123;&#125;", serverAddress, e.getMessage());</span></span><br><span class="line"><span class="string">                       &#125; else &#123;</span></span><br><span class="line"><span class="string">                           LOG.warn("Session 0x&#123;&#125; for server &#123;&#125;, unexpected error&#123;&#125;",</span></span><br><span class="line"><span class="string">                                           Long.toHexString(getSessionId()),</span></span><br><span class="line"><span class="string">                                           serverAddress,</span></span><br><span class="line"><span class="string">                                           RETRY_CONN_MSG,</span></span><br><span class="line"><span class="string">                                           e);</span></span><br><span class="line"><span class="string">                       &#125;</span></span><br><span class="line"><span class="string">                       // At this point, there might still be new packets appended to outgoingQueue.</span></span><br><span class="line"><span class="string">                       // they will be handled in next connection or cleared up if closed.</span></span><br><span class="line"><span class="string">                       cleanAndNotifyState();</span></span><br><span class="line"><span class="string">                   &#125;</span></span><br><span class="line"><span class="string">               &#125;</span></span><br><span class="line"><span class="string">           &#125;</span></span><br><span class="line"><span class="string">           synchronized (state) &#123;</span></span><br><span class="line"><span class="string">               // When it comes to this point, it guarantees that later queued</span></span><br><span class="line"><span class="string">               // packet to outgoingQueue will be notified of death.</span></span><br><span class="line"><span class="string">               cleanup();</span></span><br><span class="line"><span class="string">           &#125;</span></span><br><span class="line"><span class="string">           clientCnxnSocket.close();</span></span><br><span class="line"><span class="string">           if (state.isAlive()) &#123;</span></span><br><span class="line"><span class="string">               eventThread.queueEvent(new WatchedEvent(Event.EventType.None,</span></span><br><span class="line"><span class="string">                       Event.KeeperState.Disconnected, null));</span></span><br><span class="line"><span class="string">           &#125;</span></span><br><span class="line"><span class="string">           eventThread.queueEvent(new WatchedEvent(Event.EventType.None,</span></span><br><span class="line"><span class="string">                       Event.KeeperState.Closed, null));</span></span><br><span class="line"><span class="string">           ZooTrace.logTraceMessage(LOG, ZooTrace.getTextTraceLevel(),</span></span><br><span class="line"><span class="string">                   "SendThread exited loop for session: 0x"</span></span><br><span class="line"><span class="string">                          + Long.toHexString(getSessionId()));</span></span><br><span class="line"><span class="string">       &#125;</span></span><br></pre></td></tr></table></figure></p>
<p>netty相关的类ClientCnxnSocketNetty中对doTransport的实现为：<br>从outgoingQueue里取出一个待发送的packet，如果不为空就调用doWrite方法去发送数据包，如果当前连接已断开，将包再次加入待发送队列<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">void doTransport(int waitTimeOut,</span><br><span class="line">                    List&lt;Packet&gt; pendingQueue,</span><br><span class="line">                    ClientCnxn cnxn)</span><br><span class="line">           throws IOException, InterruptedException &#123;</span><br><span class="line">       try &#123;</span><br><span class="line">           <span class="keyword">if</span> (!firstConnect.await(waitTimeOut, TimeUnit.MILLISECONDS)) &#123;</span><br><span class="line">               <span class="built_in">return</span>;</span><br><span class="line">           &#125;</span><br><span class="line">           Packet head = null;</span><br><span class="line">           <span class="keyword">if</span> (needSasl.get()) &#123;</span><br><span class="line">               <span class="keyword">if</span> (!waitSasl.tryAcquire(waitTimeOut, TimeUnit.MILLISECONDS)) &#123;</span><br><span class="line">                   <span class="built_in">return</span>;</span><br><span class="line">               &#125;</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">               head = outgoingQueue.poll(waitTimeOut, TimeUnit.MILLISECONDS);</span><br><span class="line">           &#125;</span><br><span class="line">           // check <span class="keyword">if</span> being waken up on closing.</span><br><span class="line">           <span class="keyword">if</span> (!sendThread.getZkState().isAlive()) &#123;</span><br><span class="line">               // adding back the packet to notify of failure <span class="keyword">in</span> conLossPacket().</span><br><span class="line">               addBack(head);</span><br><span class="line">               <span class="built_in">return</span>;</span><br><span class="line">           &#125;</span><br><span class="line">           // channel disconnection happened</span><br><span class="line">           <span class="keyword">if</span> (disconnected.get()) &#123;</span><br><span class="line">               addBack(head);</span><br><span class="line">               throw new EndOfStreamException(<span class="string">"channel for sessionid 0x"</span></span><br><span class="line">                       + Long.toHexString(sessionId)</span><br><span class="line">                       + <span class="string">" is lost"</span>);</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="keyword">if</span> (head != null) &#123;</span><br><span class="line">               doWrite(pendingQueue, head, cnxn);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125; finally &#123;</span><br><span class="line">           updateNow();</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>doWrite方法会将除了outgoingQueue里的唤醒包和pin包以及链接登陆的auth包以外的全部包和调用方法带过来的packet加入事件队列<br>（因为pin包和auth包实际没有需要返回的数据信息response回调用方，pin包无处理，auth包只需要失败时的一个响应，收到auth包的失败信息，添加一个authFailed的事件到事件队列，源码里也是这么做的*在sendThread的readResponse方法）<br>并且outgoingQueue里的packet，除了唤醒包以外，pin包、auth包以及剩下的packet和调用方法带过来的packet都会被发送<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">	private void doWrite(List&lt;Packet&gt; pendingQueue, Packet p, ClientCnxn cnxn) &#123;</span><br><span class="line">        updateNow();</span><br><span class="line">        boolean anyPacketsSent = <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (p != WakeupPacket.getInstance()) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((p.requestHeader != null) &amp;&amp;</span><br><span class="line">                        (p.requestHeader.getType() != ZooDefs.OpCode.ping) &amp;&amp;</span><br><span class="line">                        (p.requestHeader.getType() != ZooDefs.OpCode.auth)) &#123;</span><br><span class="line">                    p.requestHeader.setXid(cnxn.getXid());</span><br><span class="line">                    synchronized (pendingQueue) &#123;</span><br><span class="line">                        pendingQueue.add(p);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                sendPktOnly(p);</span><br><span class="line">                anyPacketsSent = <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (outgoingQueue.isEmpty()) &#123;</span><br><span class="line">                <span class="built_in">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            p = outgoingQueue.remove();</span><br><span class="line">        &#125;</span><br><span class="line">        // TODO: maybe we should flush <span class="keyword">in</span> the loop above every N packets/bytes?</span><br><span class="line">        // But, how <span class="keyword">do</span> we determine the right value <span class="keyword">for</span> N ...</span><br><span class="line">        <span class="keyword">if</span> (anyPacketsSent) &#123;</span><br><span class="line">            channel.flush();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>当netty的channel读到数据时，会调用sendThread的readResponse方法装载响应，最后会调用wakeupCnxn往outgoingQueue里添加唤醒包<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception &#123;</span><br><span class="line">	updateNow();</span><br><span class="line">	<span class="keyword">while</span> (buf.isReadable()) &#123;</span><br><span class="line">		<span class="keyword">if</span> (incomingBuffer.remaining() &gt; buf.readableBytes()) &#123;</span><br><span class="line">			int newLimit = incomingBuffer.position()</span><br><span class="line">					+ buf.readableBytes();</span><br><span class="line">			incomingBuffer.limit(newLimit);</span><br><span class="line">		&#125;</span><br><span class="line">		buf.readBytes(incomingBuffer);</span><br><span class="line">		incomingBuffer.limit(incomingBuffer.capacity());</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (!incomingBuffer.hasRemaining()) &#123;</span><br><span class="line">			incomingBuffer.flip();</span><br><span class="line">			<span class="keyword">if</span> (incomingBuffer == lenBuffer) &#123;</span><br><span class="line">				recvCount.getAndIncrement();</span><br><span class="line">				readLength();</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (!initialized) &#123;</span><br><span class="line">				readConnectResult();</span><br><span class="line">				lenBuffer.clear();</span><br><span class="line">				incomingBuffer = lenBuffer;</span><br><span class="line">				initialized = <span class="literal">true</span>;</span><br><span class="line">				updateLastHeard();</span><br><span class="line">			&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				sendThread.readResponse(incomingBuffer);</span><br><span class="line">				lenBuffer.clear();</span><br><span class="line">				incomingBuffer = lenBuffer;</span><br><span class="line">				updateLastHeard();</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	wakeupCnxn();</span><br><span class="line">	// Note: SimpleChannelInboundHandler releases the ByteBuf <span class="keyword">for</span> us</span><br><span class="line">	// so we don<span class="string">'t need to do it.</span></span><br><span class="line"><span class="string">   &#125;</span></span><br><span class="line"><span class="string">   private void wakeupCnxn() &#123;</span></span><br><span class="line"><span class="string">       if (needSasl.get()) &#123;</span></span><br><span class="line"><span class="string">           waitSasl.release();</span></span><br><span class="line"><span class="string">       &#125;</span></span><br><span class="line"><span class="string">       outgoingQueue.add(WakeupPacket.getInstance());</span></span><br><span class="line"><span class="string">   &#125;</span></span><br></pre></td></tr></table></figure></p>
<p>sendThread的readResponse方法根据响应的数据报类型做处理<br>如果是pin包不做处理只做日志，并返回<br>如果是auth包，做日志处理，判断err码，如果auth失败了，就往事件队列里增加一个type为None，stat为AuthFailed的事件，并返回<br>如果是主动监听的服务端通知事件，创建一个WatcherEvent反序列化response的信息，并用一个WatchedEvent包装以后，加入事件队列，并返回<br>如果不满足以上的，先判断如果当前正在进行SASL身份验证，构造和立即发送响应数据包，而不是排队响应其他数据包<br>剩下的进行普通数据报处理，从pendingQueue的首部取出一个已发送待响应的packet（因为是从头拿，所以是最早发送的带响应packet）<br>判断取出的packet与读到的ReplyHeader数据的xid是否相等，不相等说明出现丢包，抛出异常<br>xid相等则将本次数据的response信息反序列化装载到packet的resonse成员<br>无论是否丢包，最终会调用finishPacket(packet)对packet做善后工作<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br></pre></td><td class="code"><pre><span class="line">void readResponse(ByteBuffer incomingBuffer) throws IOException &#123;</span><br><span class="line">           ByteBufferInputStream bbis = new ByteBufferInputStream(</span><br><span class="line">                   incomingBuffer);</span><br><span class="line">           BinaryInputArchive bbia = BinaryInputArchive.getArchive(bbis);</span><br><span class="line">           ReplyHeader replyHdr = new ReplyHeader();</span><br><span class="line"></span><br><span class="line">           replyHdr.deserialize(bbia, <span class="string">"header"</span>);</span><br><span class="line">           <span class="keyword">if</span> (replyHdr.getXid() == -2) &#123;</span><br><span class="line">               // -2 is the xid <span class="keyword">for</span> pings</span><br><span class="line">               <span class="keyword">if</span> (LOG.isDebugEnabled()) &#123;</span><br><span class="line">                   LOG.debug(<span class="string">"Got ping response for sessionid: 0x"</span></span><br><span class="line">                           + Long.toHexString(sessionId)</span><br><span class="line">                           + <span class="string">" after "</span></span><br><span class="line">                           + ((System.nanoTime() - lastPingSentNs) / 1000000)</span><br><span class="line">                           + <span class="string">"ms"</span>);</span><br><span class="line">               &#125;</span><br><span class="line">               <span class="built_in">return</span>;</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="keyword">if</span> (replyHdr.getXid() == -4) &#123;</span><br><span class="line">               // -4 is the xid <span class="keyword">for</span> AuthPacket               </span><br><span class="line">               <span class="keyword">if</span>(replyHdr.getErr() == KeeperException.Code.AUTHFAILED.intValue()) &#123;</span><br><span class="line">                   state = States.AUTH_FAILED;                    </span><br><span class="line">                   eventThread.queueEvent( new WatchedEvent(Watcher.Event.EventType.None, </span><br><span class="line">                           Watcher.Event.KeeperState.AuthFailed, null) );</span><br><span class="line">                   eventThread.queueEventOfDeath();</span><br><span class="line">               &#125;</span><br><span class="line">               <span class="keyword">if</span> (LOG.isDebugEnabled()) &#123;</span><br><span class="line">                   LOG.debug(<span class="string">"Got auth sessionid:0x"</span></span><br><span class="line">                           + Long.toHexString(sessionId));</span><br><span class="line">               &#125;</span><br><span class="line">               <span class="built_in">return</span>;</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="keyword">if</span> (replyHdr.getXid() == -1) &#123;</span><br><span class="line">               // -1 means notification</span><br><span class="line">               <span class="keyword">if</span> (LOG.isDebugEnabled()) &#123;</span><br><span class="line">                   LOG.debug(<span class="string">"Got notification sessionid:0x"</span></span><br><span class="line">                       + Long.toHexString(sessionId));</span><br><span class="line">               &#125;</span><br><span class="line">               WatcherEvent event = new WatcherEvent();</span><br><span class="line">               event.deserialize(bbia, <span class="string">"response"</span>);</span><br><span class="line"></span><br><span class="line">               // convert from a server path to a client path</span><br><span class="line">               <span class="keyword">if</span> (chrootPath != null) &#123;</span><br><span class="line">                   String serverPath = event.getPath();</span><br><span class="line">                   <span class="keyword">if</span>(serverPath.compareTo(chrootPath)==0)</span><br><span class="line">                       event.setPath(<span class="string">"/"</span>);</span><br><span class="line">                   <span class="keyword">else</span> <span class="keyword">if</span> (serverPath.length() &gt; chrootPath.length())</span><br><span class="line">                       event.setPath(serverPath.substring(chrootPath.length()));</span><br><span class="line">                   <span class="keyword">else</span> &#123;</span><br><span class="line">                   	LOG.warn(<span class="string">"Got server path "</span> + event.getPath()</span><br><span class="line">                   			+ <span class="string">" which is too short for chroot path "</span></span><br><span class="line">                   			+ chrootPath);</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               WatchedEvent we = new WatchedEvent(event);</span><br><span class="line">               <span class="keyword">if</span> (LOG.isDebugEnabled()) &#123;</span><br><span class="line">                   LOG.debug(<span class="string">"Got "</span> + we + <span class="string">" for sessionid 0x"</span></span><br><span class="line">                           + Long.toHexString(sessionId));</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               eventThread.queueEvent( we );</span><br><span class="line">               <span class="built_in">return</span>;</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           // If SASL authentication is currently <span class="keyword">in</span> progress, construct and</span><br><span class="line">           // send a response packet immediately, rather than queuing a</span><br><span class="line">           // response as with other packets.</span><br><span class="line">           <span class="keyword">if</span> (tunnelAuthInProgress()) &#123;</span><br><span class="line">               GetSASLRequest request = new GetSASLRequest();</span><br><span class="line">               request.deserialize(bbia,<span class="string">"token"</span>);</span><br><span class="line">               zooKeeperSaslClient.respondToServer(request.getToken(),</span><br><span class="line">                 ClientCnxn.this);</span><br><span class="line">               <span class="built_in">return</span>;</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           Packet packet;</span><br><span class="line">           synchronized (pendingQueue) &#123;</span><br><span class="line">               <span class="keyword">if</span> (pendingQueue.size() == 0) &#123;</span><br><span class="line">                   throw new IOException(<span class="string">"Nothing in the queue, but got "</span></span><br><span class="line">                           + replyHdr.getXid());</span><br><span class="line">               &#125;</span><br><span class="line">               packet = pendingQueue.remove();</span><br><span class="line">           &#125;</span><br><span class="line">           /*</span><br><span class="line">            * Since requests are processed <span class="keyword">in</span> order, we better get a response</span><br><span class="line">            * to the first request!</span><br><span class="line">            */</span><br><span class="line">           try &#123;</span><br><span class="line">               <span class="keyword">if</span> (packet.requestHeader.getXid() != replyHdr.getXid()) &#123;</span><br><span class="line">                   packet.replyHeader.setErr(</span><br><span class="line">                           KeeperException.Code.CONNECTIONLOSS.intValue());</span><br><span class="line">                   throw new IOException(<span class="string">"Xid out of order. Got Xid "</span></span><br><span class="line">                           + replyHdr.getXid() + <span class="string">" with err "</span> +</span><br><span class="line">                           + replyHdr.getErr() +</span><br><span class="line">                           <span class="string">" expected Xid "</span></span><br><span class="line">                           + packet.requestHeader.getXid()</span><br><span class="line">                           + <span class="string">" for a packet with details: "</span></span><br><span class="line">                           + packet );</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               packet.replyHeader.setXid(replyHdr.getXid());</span><br><span class="line">               packet.replyHeader.setErr(replyHdr.getErr());</span><br><span class="line">               packet.replyHeader.setZxid(replyHdr.getZxid());</span><br><span class="line">               <span class="keyword">if</span> (replyHdr.getZxid() &gt; 0) &#123;</span><br><span class="line">                   lastZxid = replyHdr.getZxid();</span><br><span class="line">               &#125;</span><br><span class="line">               <span class="keyword">if</span> (packet.response != null &amp;&amp; replyHdr.getErr() == 0) &#123;</span><br><span class="line">                   packet.response.deserialize(bbia, <span class="string">"response"</span>);</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               <span class="keyword">if</span> (LOG.isDebugEnabled()) &#123;</span><br><span class="line">                   LOG.debug(<span class="string">"Reading reply sessionid:0x"</span></span><br><span class="line">                           + Long.toHexString(sessionId) + <span class="string">", packet:: "</span> + packet);</span><br><span class="line">               &#125;</span><br><span class="line">           &#125; finally &#123;</span><br><span class="line">               finishPacket(packet);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br></pre></td></tr></table></figure></p>
<p>在eventThread的queueEvent方法里，会调用watcher.materialize(event.getState(),event.getType(), event.getPath())识别事件<br>构建一个WatcherSetEventPair加入事件队列waitingEvents<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">private final LinkedBlockingQueue&lt;Object&gt; waitingEvents =</span><br><span class="line">           new LinkedBlockingQueue&lt;Object&gt;();</span><br><span class="line">public void queueEvent(WatchedEvent event) &#123;</span><br><span class="line">	queueEvent(event, null);</span><br><span class="line">&#125;</span><br><span class="line">private void queueEvent(WatchedEvent event,</span><br><span class="line">		Set&lt;Watcher&gt; materializedWatchers) &#123;</span><br><span class="line">	<span class="keyword">if</span> (event.getType() == EventType.None</span><br><span class="line">			&amp;&amp; sessionState == event.getState()) &#123;</span><br><span class="line">		<span class="built_in">return</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	sessionState = event.getState();</span><br><span class="line">	final Set&lt;Watcher&gt; watchers;</span><br><span class="line">	<span class="keyword">if</span> (materializedWatchers == null) &#123;</span><br><span class="line">		// materialize the watchers based on the event</span><br><span class="line">		watchers = watcher.materialize(event.getState(),</span><br><span class="line">				event.getType(), event.getPath());</span><br><span class="line">	&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		watchers = new HashSet&lt;Watcher&gt;();</span><br><span class="line">		watchers.addAll(materializedWatchers);</span><br><span class="line">	&#125;</span><br><span class="line">	WatcherSetEventPair pair = new WatcherSetEventPair(watchers, event);</span><br><span class="line">	// queue the pair (watch <span class="built_in">set</span> &amp; event) <span class="keyword">for</span> later processing</span><br><span class="line">	waitingEvents.add(pair);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>eventThread使用的watcher实际为Zookeeper类传递给ClientCnxn的watcherManager成员<br>也就是zookeeper本地注册watcher的对象<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">protected ClientCnxn createConnection(String chrootPath,</span><br><span class="line">        HostProvider hostProvider, int sessionTimeout, ZooKeeper zooKeeper,</span><br><span class="line">        ClientWatchManager watcher, ClientCnxnSocket clientCnxnSocket,</span><br><span class="line">        boolean canBeReadOnly) throws IOException &#123;</span><br><span class="line">    <span class="built_in">return</span> new ClientCnxn(chrootPath, hostProvider, sessionTimeout, this,</span><br><span class="line">            watchManager, clientCnxnSocket, canBeReadOnly);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">   public ClientCnxn(String chrootPath, HostProvider hostProvider, int sessionTimeout, ZooKeeper zooKeeper,</span><br><span class="line">           ClientWatchManager watcher, ClientCnxnSocket clientCnxnSocket, boolean canBeReadOnly)</span><br><span class="line">           throws IOException &#123;</span><br><span class="line">       this(chrootPath, hostProvider, sessionTimeout, zooKeeper, watcher,</span><br><span class="line">            clientCnxnSocket, 0, new byte[16], canBeReadOnly);</span><br><span class="line">   &#125;</span><br><span class="line">public ClientCnxn(String chrootPath, HostProvider hostProvider, int sessionTimeout, ZooKeeper zooKeeper,</span><br><span class="line">           ClientWatchManager watcher, ClientCnxnSocket clientCnxnSocket,</span><br><span class="line">           long sessionId, byte[] sessionPasswd, boolean canBeReadOnly) &#123;</span><br><span class="line">       this.zooKeeper = zooKeeper;</span><br><span class="line">       this.watcher = watcher;</span><br><span class="line">       this.sessionId = sessionId;</span><br><span class="line">       this.sessionPasswd = sessionPasswd;</span><br><span class="line">       this.sessionTimeout = sessionTimeout;</span><br><span class="line">       this.hostProvider = hostProvider;</span><br><span class="line">       this.chrootPath = chrootPath;</span><br><span class="line"></span><br><span class="line">       connectTimeout = sessionTimeout / hostProvider.size();</span><br><span class="line">       readTimeout = sessionTimeout * 2 / 3;</span><br><span class="line">       readOnly = canBeReadOnly;</span><br><span class="line"></span><br><span class="line">       sendThread = new SendThread(clientCnxnSocket);</span><br><span class="line">       eventThread = new EventThread();</span><br><span class="line">       this.clientConfig=zooKeeper.getClientConfig();</span><br><span class="line">       initRequestTimeout();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<p>ZkWatcherManager使用三个集合成员存储不同事件类型的watcher，并设置一个默认的watcher<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">static class ZKWatchManager implements ClientWatchManager &#123;</span><br><span class="line">       private final Map&lt;String, Set&lt;Watcher&gt;&gt; dataWatches =</span><br><span class="line">           new HashMap&lt;String, Set&lt;Watcher&gt;&gt;();</span><br><span class="line">       private final Map&lt;String, Set&lt;Watcher&gt;&gt; existWatches =</span><br><span class="line">           new HashMap&lt;String, Set&lt;Watcher&gt;&gt;();</span><br><span class="line">       private final Map&lt;String, Set&lt;Watcher&gt;&gt; childWatches =</span><br><span class="line">           new HashMap&lt;String, Set&lt;Watcher&gt;&gt;();</span><br><span class="line">       private boolean disableAutoWatchReset;</span><br><span class="line"></span><br><span class="line">       ZKWatchManager(boolean disableAutoWatchReset) &#123;</span><br><span class="line">           this.disableAutoWatchReset = disableAutoWatchReset;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       protected volatile Watcher defaultWatcher;</span><br></pre></td></tr></table></figure></p>
<p>其materialize方法根据事件类型和路径信息，匹配到对应的watcher事件并返回事件集合，可以看到使用remove方法获得事件集合。这也是为什么注册的事件只响应一次<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line">public Set&lt;Watcher&gt; materialize(Watcher.Event.KeeperState state,</span><br><span class="line">                                       Watcher.Event.EventType <span class="built_in">type</span>,</span><br><span class="line">                                       String clientPath)</span><br><span class="line">       &#123;</span><br><span class="line">           Set&lt;Watcher&gt; result = new HashSet&lt;Watcher&gt;();</span><br><span class="line"></span><br><span class="line">           switch (<span class="built_in">type</span>) &#123;</span><br><span class="line">           <span class="keyword">case</span> None:</span><br><span class="line">               result.add(defaultWatcher);</span><br><span class="line">               boolean clear = disableAutoWatchReset &amp;&amp; state != Watcher.Event.KeeperState.SyncConnected;</span><br><span class="line">               synchronized(dataWatches) &#123;</span><br><span class="line">                   <span class="keyword">for</span>(Set&lt;Watcher&gt; ws: dataWatches.values()) &#123;</span><br><span class="line">                       result.addAll(ws);</span><br><span class="line">                   &#125;</span><br><span class="line">                   <span class="keyword">if</span> (clear) &#123;</span><br><span class="line">                       dataWatches.clear();</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               synchronized(existWatches) &#123;</span><br><span class="line">                   <span class="keyword">for</span>(Set&lt;Watcher&gt; ws: existWatches.values()) &#123;</span><br><span class="line">                       result.addAll(ws);</span><br><span class="line">                   &#125;</span><br><span class="line">                   <span class="keyword">if</span> (clear) &#123;</span><br><span class="line">                       existWatches.clear();</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               synchronized(childWatches) &#123;</span><br><span class="line">                   <span class="keyword">for</span>(Set&lt;Watcher&gt; ws: childWatches.values()) &#123;</span><br><span class="line">                       result.addAll(ws);</span><br><span class="line">                   &#125;</span><br><span class="line">                   <span class="keyword">if</span> (clear) &#123;</span><br><span class="line">                       childWatches.clear();</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               <span class="built_in">return</span> result;</span><br><span class="line">           <span class="keyword">case</span> NodeDataChanged:</span><br><span class="line">           <span class="keyword">case</span> NodeCreated:</span><br><span class="line">               synchronized (dataWatches) &#123;</span><br><span class="line">                   addTo(dataWatches.remove(clientPath), result);</span><br><span class="line">               &#125;</span><br><span class="line">               synchronized (existWatches) &#123;</span><br><span class="line">                   addTo(existWatches.remove(clientPath), result);</span><br><span class="line">               &#125;</span><br><span class="line">               <span class="built_in">break</span>;</span><br><span class="line">           <span class="keyword">case</span> NodeChildrenChanged:</span><br><span class="line">               synchronized (childWatches) &#123;</span><br><span class="line">                   addTo(childWatches.remove(clientPath), result);</span><br><span class="line">               &#125;</span><br><span class="line">               <span class="built_in">break</span>;</span><br><span class="line">           <span class="keyword">case</span> NodeDeleted:</span><br><span class="line">               synchronized (dataWatches) &#123;</span><br><span class="line">                   addTo(dataWatches.remove(clientPath), result);</span><br><span class="line">               &#125;</span><br><span class="line">               // XXX This shouldn<span class="string">'t be needed, but just in case</span></span><br><span class="line"><span class="string">               synchronized (existWatches) &#123;</span></span><br><span class="line"><span class="string">                   Set&lt;Watcher&gt; list = existWatches.remove(clientPath);</span></span><br><span class="line"><span class="string">                   if (list != null) &#123;</span></span><br><span class="line"><span class="string">                       addTo(list, result);</span></span><br><span class="line"><span class="string">                       LOG.warn("We are triggering an exists watch for delete! Shouldn'</span>t happen!<span class="string">");</span></span><br><span class="line"><span class="string">                   &#125;</span></span><br><span class="line"><span class="string">               &#125;</span></span><br><span class="line"><span class="string">               synchronized (childWatches) &#123;</span></span><br><span class="line"><span class="string">                   addTo(childWatches.remove(clientPath), result);</span></span><br><span class="line"><span class="string">               &#125;</span></span><br><span class="line"><span class="string">               break;</span></span><br><span class="line"><span class="string">           default:</span></span><br><span class="line"><span class="string">               String msg = "</span>Unhandled watch event <span class="built_in">type</span> <span class="string">" + type</span></span><br><span class="line"><span class="string">                   + "</span> with state <span class="string">" + state + "</span> on path <span class="string">" + clientPath;</span></span><br><span class="line"><span class="string">               LOG.error(msg);</span></span><br><span class="line"><span class="string">               throw new RuntimeException(msg);</span></span><br><span class="line"><span class="string">           &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">           return result;</span></span><br><span class="line"><span class="string">       &#125;</span></span><br></pre></td></tr></table></figure></p>
<p>说回readResponse最后对数据报的处理，在完成将数据报对应的本地注册事件加入事件队列以后，执行finishPacket方法<br>在finishPacket中，设置packet的finished标志位true<br>如果该packet没有绑定异步回调函数，则直接调用packet的notify唤醒（因为是异步发送，前面获得packet以后会陷入阻塞wait）<br>否则调用eventThread.queuePacket方法对响应后的数据包做进一步处理<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">protected void finishPacket(Packet p) &#123;</span><br><span class="line">       int err = p.replyHeader.getErr();</span><br><span class="line">       <span class="keyword">if</span> (p.watchRegistration != null) &#123;</span><br><span class="line">           p.watchRegistration.register(err);</span><br><span class="line">       &#125;</span><br><span class="line">       // Add all the removed watch events to the event queue, so that the</span><br><span class="line">       // clients will be notified with <span class="string">'Data/Child WatchRemoved'</span> event <span class="built_in">type</span>.</span><br><span class="line">       <span class="keyword">if</span> (p.watchDeregistration != null) &#123;</span><br><span class="line">           Map&lt;EventType, Set&lt;Watcher&gt;&gt; materializedWatchers = null;</span><br><span class="line">           try &#123;</span><br><span class="line">               materializedWatchers = p.watchDeregistration.unregister(err);</span><br><span class="line">               <span class="keyword">for</span> (Entry&lt;EventType, Set&lt;Watcher&gt;&gt; entry : materializedWatchers</span><br><span class="line">                       .entrySet()) &#123;</span><br><span class="line">                   Set&lt;Watcher&gt; watchers = entry.getValue();</span><br><span class="line">                   <span class="keyword">if</span> (watchers.size() &gt; 0) &#123;</span><br><span class="line">                       queueEvent(p.watchDeregistration.getClientPath(), err,</span><br><span class="line">                               watchers, entry.getKey());</span><br><span class="line">                       // ignore connectionloss when removing from <span class="built_in">local</span></span><br><span class="line">                       // session</span><br><span class="line">                       p.replyHeader.setErr(Code.OK.intValue());</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;</span><br><span class="line">           &#125; catch (KeeperException.NoWatcherException nwe) &#123;</span><br><span class="line">               p.replyHeader.setErr(nwe.code().intValue());</span><br><span class="line">           &#125; catch (KeeperException ke) &#123;</span><br><span class="line">               p.replyHeader.setErr(ke.code().intValue());</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (p.cb == null) &#123;</span><br><span class="line">           synchronized (p) &#123;</span><br><span class="line">               p.finished = <span class="literal">true</span>;</span><br><span class="line">               p.notifyAll();</span><br><span class="line">           &#125;</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           p.finished = <span class="literal">true</span>;</span><br><span class="line">           eventThread.queuePacket(p);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>eventThread的queuePacket方法先判断当前是否已经被要求结束，如果是，判断是否还没有停止运行，没有则将packet加入待处理队列，否则直接调用processEvent处理packet<br>（因为在设置wasKilled为true后，run方法里会将waitingEvents里事件处理完后才设置停止运行并结束线程）<br>如果wasKilled不为true，处于运行中，直接将packet加入waitingEvents<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">public void queuePacket(Packet packet) &#123;</span><br><span class="line">  <span class="keyword">if</span> (wasKilled) &#123;</span><br><span class="line">	 synchronized (waitingEvents) &#123;</span><br><span class="line">		<span class="keyword">if</span> (isRunning) waitingEvents.add(packet);</span><br><span class="line">		<span class="keyword">else</span> processEvent(packet);</span><br><span class="line">	 &#125;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">	 waitingEvents.add(packet);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>eventThread的run方法，循环从waitingEvents里（事件队列，是个阻塞队列）取出待处理的事件<br>调用processEvent去处理事件，如果收到结束事件（eventOfDeath），设置wasKilled为true，在处理完waitingEvents的事件后，停止运行（isRunnig设置为false）<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">public void <span class="function"><span class="title">run</span></span>() &#123;</span><br><span class="line">   try &#123;</span><br><span class="line">	  isRunning = <span class="literal">true</span>;</span><br><span class="line">	  <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">		 Object event = waitingEvents.take();</span><br><span class="line">		 <span class="keyword">if</span> (event == eventOfDeath) &#123;</span><br><span class="line">			wasKilled = <span class="literal">true</span>;</span><br><span class="line">		 &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			processEvent(event);</span><br><span class="line">		 &#125;</span><br><span class="line">		 <span class="keyword">if</span> (wasKilled)</span><br><span class="line">			synchronized (waitingEvents) &#123;</span><br><span class="line">			   <span class="keyword">if</span> (waitingEvents.isEmpty()) &#123;</span><br><span class="line">				  isRunning = <span class="literal">false</span>;</span><br><span class="line">				  <span class="built_in">break</span>;</span><br><span class="line">			   &#125;</span><br><span class="line">			&#125;</span><br><span class="line">	  &#125;</span><br><span class="line">   &#125; catch (InterruptedException e) &#123;</span><br><span class="line">	  LOG.error(<span class="string">"Event thread exiting due to interruption"</span>, e);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">	LOG.info(<span class="string">"EventThread shut down for session: 0x&#123;&#125;"</span>,</span><br><span class="line">			 Long.toHexString(getSessionId()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>在查看processEvent方法前，我们先回过头来看执行各个语句的源码</p>
<h3 id="create"><a href="#create" class="headerlink" title="create"></a>create</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">public String create(final String path, byte data[], List&lt;ACL&gt; acl,</span><br><span class="line">           CreateMode createMode, Stat <span class="built_in">stat</span>, long ttl)</span><br><span class="line">           throws KeeperException, InterruptedException &#123;</span><br><span class="line">       final String clientPath = path;</span><br><span class="line">       PathUtils.validatePath(clientPath, createMode.isSequential());</span><br><span class="line">       EphemeralType.validateTTL(createMode, ttl);</span><br><span class="line"></span><br><span class="line">       final String serverPath = prependChroot(clientPath);</span><br><span class="line"></span><br><span class="line">       RequestHeader h = new RequestHeader();</span><br><span class="line">       setCreateHeader(createMode, h);</span><br><span class="line">       Create2Response response = new Create2Response();</span><br><span class="line">       <span class="keyword">if</span> (acl != null &amp;&amp; acl.size() == 0) &#123;</span><br><span class="line">           throw new KeeperException.InvalidACLException();</span><br><span class="line">       &#125;</span><br><span class="line">       Record record = makeCreateRecord(createMode, serverPath, data, acl, ttl);</span><br><span class="line">       ReplyHeader r = cnxn.submitRequest(h, record, response, null);</span><br><span class="line">       <span class="keyword">if</span> (r.getErr() != 0) &#123;</span><br><span class="line">           throw KeeperException.create(KeeperException.Code.get(r.getErr()),</span><br><span class="line">                   clientPath);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">if</span> (<span class="built_in">stat</span> != null) &#123;</span><br><span class="line">           DataTree.copyStat(response.getStat(), <span class="built_in">stat</span>);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">if</span> (cnxn.chrootPath == null) &#123;</span><br><span class="line">           <span class="built_in">return</span> response.getPath();</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           <span class="built_in">return</span> response.getPath().substring(cnxn.chrootPath.length());</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<h3 id="setData-带回调"><a href="#setData-带回调" class="headerlink" title="setData(带回调)"></a>setData(带回调)</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">public void setData(final String path, byte data[], int version,</span><br><span class="line">           StatCallback cb, Object ctx)</span><br><span class="line">   &#123;</span><br><span class="line">       final String clientPath = path;</span><br><span class="line">       PathUtils.validatePath(clientPath);</span><br><span class="line"></span><br><span class="line">       final String serverPath = prependChroot(clientPath);</span><br><span class="line"></span><br><span class="line">       RequestHeader h = new RequestHeader();</span><br><span class="line">       h.setType(ZooDefs.OpCode.setData);</span><br><span class="line">       SetDataRequest request = new SetDataRequest();</span><br><span class="line">       request.setPath(serverPath);</span><br><span class="line">       request.setData(data);</span><br><span class="line">       request.setVersion(version);</span><br><span class="line">       SetDataResponse response = new SetDataResponse();</span><br><span class="line">       cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,</span><br><span class="line">               clientPath, serverPath, ctx, null);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<h3 id="getData"><a href="#getData" class="headerlink" title="getData"></a>getData</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">public byte[] getData(final String path, Watcher watcher, Stat <span class="built_in">stat</span>)</span><br><span class="line">       throws KeeperException, InterruptedException</span><br><span class="line">    &#123;</span><br><span class="line">       final String clientPath = path;</span><br><span class="line">       PathUtils.validatePath(clientPath);</span><br><span class="line"></span><br><span class="line">       // the watch contains the un-chroot path</span><br><span class="line">       WatchRegistration wcb = null;</span><br><span class="line">       <span class="keyword">if</span> (watcher != null) &#123;</span><br><span class="line">           wcb = new DataWatchRegistration(watcher, clientPath);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       final String serverPath = prependChroot(clientPath);</span><br><span class="line"></span><br><span class="line">       RequestHeader h = new RequestHeader();</span><br><span class="line">       h.setType(ZooDefs.OpCode.getData);</span><br><span class="line">       GetDataRequest request = new GetDataRequest();</span><br><span class="line">       request.setPath(serverPath);</span><br><span class="line">       request.setWatch(watcher != null);</span><br><span class="line">       GetDataResponse response = new GetDataResponse();</span><br><span class="line">       ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);</span><br><span class="line">       <span class="keyword">if</span> (r.getErr() != 0) &#123;</span><br><span class="line">           throw KeeperException.create(KeeperException.Code.get(r.getErr()),</span><br><span class="line">                   clientPath);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">if</span> (<span class="built_in">stat</span> != null) &#123;</span><br><span class="line">           DataTree.copyStat(response.getStat(), <span class="built_in">stat</span>);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="built_in">return</span> response.getData();</span><br><span class="line">   &#125;</span><br><span class="line">.....</span><br></pre></td></tr></table></figure>
<p>可以看见都是先进行下路径的校验，然后生成一个RequestHeader,为请求头设置操作的类型<br>对于有接受stat参数的，最后会调用DataTree.copyStat(response.getStat(), stat)把最新的stat数据复制给传入的stat对象<br>根据类型生成不同的Record实现类，record接口提供两个方法，序列化与反序列化，一个Reqeust实现类实例以及一个Response实现类实例<br>当有回调函数时，将回调函数传入<br>最终都是调用ClientCnxn的submitRequest方法，获得ReplyHeader（实际的response数据会装载到传入的response对象里）<br>ReplyHeader只保存了是否出现失败，以及错误码和路径等信息，不保存实际返回的响应数据<br>submitRequest实际会调用queuePacket方法去发送给服务端，并获得及时响应的packet对象<br>因为是异步发送，后面会根据是否设置了超时时间选择调用wait阻塞等待或者等待超时设置replyHeader失败并返回<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">public ReplyHeader submitRequest(RequestHeader h, Record request,</span><br><span class="line">        Record response, WatchRegistration watchRegistration)</span><br><span class="line">        throws InterruptedException &#123;</span><br><span class="line">    <span class="built_in">return</span> submitRequest(h, request, response, watchRegistration, null);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public ReplyHeader submitRequest(RequestHeader h, Record request,</span><br><span class="line">        Record response, WatchRegistration watchRegistration,</span><br><span class="line">        WatchDeregistration watchDeregistration)</span><br><span class="line">        throws InterruptedException &#123;</span><br><span class="line">    ReplyHeader r = new ReplyHeader();</span><br><span class="line">    Packet packet = queuePacket(h, r, request, response, null, null, null,</span><br><span class="line">            null, watchRegistration, watchDeregistration);</span><br><span class="line">    synchronized (packet) &#123;</span><br><span class="line">        <span class="keyword">if</span> (requestTimeout &gt; 0) &#123;</span><br><span class="line">            // Wait <span class="keyword">for</span> request completion with timeout</span><br><span class="line">            waitForPacketFinish(r, packet);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            // Wait <span class="keyword">for</span> request completion infinitely</span><br><span class="line">            <span class="keyword">while</span> (!packet.finished) &#123;</span><br><span class="line">                packet.wait();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (r.getErr() == Code.REQUESTTIMEOUT.intValue()) &#123;</span><br><span class="line">        sendThread.cleanAndNotifyState();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">return</span> r;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>queuePacket实际将请求信息封装为一个packet，并将packet加入outgoingQueue队列，由sendThread去处理，返回packet对象<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">private final LinkedBlockingDeque&lt;Packet&gt; outgoingQueue = new LinkedBlockingDeque&lt;Packet&gt;();</span><br><span class="line">public Packet queuePacket(RequestHeader h, ReplyHeader r, Record request,</span><br><span class="line">           Record response, AsyncCallback cb, String clientPath,</span><br><span class="line">           String serverPath, Object ctx, WatchRegistration watchRegistration,</span><br><span class="line">           WatchDeregistration watchDeregistration) &#123;</span><br><span class="line">       Packet packet = null;</span><br><span class="line"></span><br><span class="line">       // Note that we <span class="keyword">do</span> not generate the Xid <span class="keyword">for</span> the packet yet. It is</span><br><span class="line">       // generated later at send-time, by an implementation of ClientCnxnSocket::doIO(),</span><br><span class="line">       // <span class="built_in">where</span> the packet is actually sent.</span><br><span class="line">       packet = new Packet(h, r, request, response, watchRegistration);</span><br><span class="line">       packet.cb = cb;</span><br><span class="line">       packet.ctx = ctx;</span><br><span class="line">       packet.clientPath = clientPath;</span><br><span class="line">       packet.serverPath = serverPath;</span><br><span class="line">       packet.watchDeregistration = watchDeregistration;</span><br><span class="line">       // The synchronized block here is <span class="keyword">for</span> two purpose:</span><br><span class="line">       // 1. synchronize with the final cleanup() <span class="keyword">in</span> SendThread.run() to avoid race</span><br><span class="line">       // 2. synchronized against each packet. So <span class="keyword">if</span> a closeSession packet is added,</span><br><span class="line">       // later packet will be notified.</span><br><span class="line">       synchronized (state) &#123;</span><br><span class="line">           <span class="keyword">if</span> (!state.isAlive() || closing) &#123;</span><br><span class="line">               conLossPacket(packet);</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">               // If the client is asking to close the session <span class="keyword">then</span></span><br><span class="line">               // mark as closing</span><br><span class="line">               <span class="keyword">if</span> (h.getType() == OpCode.closeSession) &#123;</span><br><span class="line">                   closing = <span class="literal">true</span>;</span><br><span class="line">               &#125;</span><br><span class="line">               outgoingQueue.add(packet);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">       sendThread.getClientCnxnSocket().packetAdded();</span><br><span class="line">       <span class="built_in">return</span> packet;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>processEvent方法根据event的类型，执行不同操作<br>如果是本地监听事件，直接调用注册的各个watcher的process方法<br>如果是本地的callBack回调函数类型，则使用instanceOf判断以后，使用不同的回调函数强制转换去执行processResult（从目前的源码分析暂时没看到有使用，可能是nio的socket实现类，这里只分析了默认的实现类ClientCnxnSocketNetty）<br>如果是response类型，使用instanceOf判断以后，选择具体的实现类去执行processResult操作<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br></pre></td><td class="code"><pre><span class="line">private void processEvent(Object event) &#123;</span><br><span class="line">  try &#123;</span><br><span class="line">	  <span class="keyword">if</span> (event instanceof WatcherSetEventPair) &#123;</span><br><span class="line">		  // each watcher will process the event</span><br><span class="line">		  WatcherSetEventPair pair = (WatcherSetEventPair) event;</span><br><span class="line">		  <span class="keyword">for</span> (Watcher watcher : pair.watchers) &#123;</span><br><span class="line">			  try &#123;</span><br><span class="line">				  watcher.process(pair.event);</span><br><span class="line">			  &#125; catch (Throwable t) &#123;</span><br><span class="line">				  LOG.error(<span class="string">"Error while calling watcher "</span>, t);</span><br><span class="line">			  &#125;</span><br><span class="line">		  &#125;</span><br><span class="line">		&#125; <span class="keyword">else</span> <span class="keyword">if</span> (event instanceof LocalCallback) &#123;</span><br><span class="line">			LocalCallback lcb = (LocalCallback) event;</span><br><span class="line">			<span class="keyword">if</span> (lcb.cb instanceof StatCallback) &#123;</span><br><span class="line">				((StatCallback) lcb.cb).processResult(lcb.rc, lcb.path,</span><br><span class="line">						lcb.ctx, null);</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (lcb.cb instanceof DataCallback) &#123;</span><br><span class="line">				((DataCallback) lcb.cb).processResult(lcb.rc, lcb.path,</span><br><span class="line">						lcb.ctx, null, null);</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (lcb.cb instanceof ACLCallback) &#123;</span><br><span class="line">				((ACLCallback) lcb.cb).processResult(lcb.rc, lcb.path,</span><br><span class="line">						lcb.ctx, null, null);</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (lcb.cb instanceof ChildrenCallback) &#123;</span><br><span class="line">				((ChildrenCallback) lcb.cb).processResult(lcb.rc,</span><br><span class="line">						lcb.path, lcb.ctx, null);</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (lcb.cb instanceof Children2Callback) &#123;</span><br><span class="line">				((Children2Callback) lcb.cb).processResult(lcb.rc,</span><br><span class="line">						lcb.path, lcb.ctx, null, null);</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (lcb.cb instanceof StringCallback) &#123;</span><br><span class="line">				((StringCallback) lcb.cb).processResult(lcb.rc,</span><br><span class="line">						lcb.path, lcb.ctx, null);</span><br><span class="line">			&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				((VoidCallback) lcb.cb).processResult(lcb.rc, lcb.path,</span><br><span class="line">						lcb.ctx);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		  Packet p = (Packet) event;</span><br><span class="line">		  int rc = 0;</span><br><span class="line">		  String clientPath = p.clientPath;</span><br><span class="line">		  <span class="keyword">if</span> (p.replyHeader.getErr() != 0) &#123;</span><br><span class="line">			  rc = p.replyHeader.getErr();</span><br><span class="line">		  &#125;</span><br><span class="line">		  <span class="keyword">if</span> (p.cb == null) &#123;</span><br><span class="line">			  LOG.warn(<span class="string">"Somehow a null cb got to EventThread!"</span>);</span><br><span class="line">		  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof ExistsResponse</span><br><span class="line">				  || p.response instanceof SetDataResponse</span><br><span class="line">				  || p.response instanceof SetACLResponse) &#123;</span><br><span class="line">			  StatCallback cb = (StatCallback) p.cb;</span><br><span class="line">			  <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">				  <span class="keyword">if</span> (p.response instanceof ExistsResponse) &#123;</span><br><span class="line">					  cb.processResult(rc, clientPath, p.ctx,</span><br><span class="line">							  ((ExistsResponse) p.response)</span><br><span class="line">									  .getStat());</span><br><span class="line">				  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof SetDataResponse) &#123;</span><br><span class="line">					  cb.processResult(rc, clientPath, p.ctx,</span><br><span class="line">							  ((SetDataResponse) p.response)</span><br><span class="line">									  .getStat());</span><br><span class="line">				  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof SetACLResponse) &#123;</span><br><span class="line">					  cb.processResult(rc, clientPath, p.ctx,</span><br><span class="line">							  ((SetACLResponse) p.response)</span><br><span class="line">									  .getStat());</span><br><span class="line">				  &#125;</span><br><span class="line">			  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, null);</span><br><span class="line">			  &#125;</span><br><span class="line">		  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof GetDataResponse) &#123;</span><br><span class="line">			  DataCallback cb = (DataCallback) p.cb;</span><br><span class="line">			  GetDataResponse rsp = (GetDataResponse) p.response;</span><br><span class="line">			  <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, rsp</span><br><span class="line">						  .getData(), rsp.getStat());</span><br><span class="line">			  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, null,</span><br><span class="line">						  null);</span><br><span class="line">			  &#125;</span><br><span class="line">		  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof GetACLResponse) &#123;</span><br><span class="line">			  ACLCallback cb = (ACLCallback) p.cb;</span><br><span class="line">			  GetACLResponse rsp = (GetACLResponse) p.response;</span><br><span class="line">			  <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, rsp</span><br><span class="line">						  .getAcl(), rsp.getStat());</span><br><span class="line">			  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, null,</span><br><span class="line">						  null);</span><br><span class="line">			  &#125;</span><br><span class="line">		  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof GetChildrenResponse) &#123;</span><br><span class="line">			  ChildrenCallback cb = (ChildrenCallback) p.cb;</span><br><span class="line">			  GetChildrenResponse rsp = (GetChildrenResponse) p.response;</span><br><span class="line">			  <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, rsp</span><br><span class="line">						  .getChildren());</span><br><span class="line">			  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, null);</span><br><span class="line">			  &#125;</span><br><span class="line">		  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof GetChildren2Response) &#123;</span><br><span class="line">			  Children2Callback cb = (Children2Callback) p.cb;</span><br><span class="line">			  GetChildren2Response rsp = (GetChildren2Response) p.response;</span><br><span class="line">			  <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, rsp</span><br><span class="line">						  .getChildren(), rsp.getStat());</span><br><span class="line">			  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, null, null);</span><br><span class="line">			  &#125;</span><br><span class="line">		  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof CreateResponse) &#123;</span><br><span class="line">			  StringCallback cb = (StringCallback) p.cb;</span><br><span class="line">			  CreateResponse rsp = (CreateResponse) p.response;</span><br><span class="line">			  <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx,</span><br><span class="line">						  (chrootPath == null</span><br><span class="line">								  ? rsp.getPath()</span><br><span class="line">								  : rsp.getPath()</span><br><span class="line">							.substring(chrootPath.length())));</span><br><span class="line">			  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, null);</span><br><span class="line">			  &#125;</span><br><span class="line">		  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof Create2Response) &#123;</span><br><span class="line">			  Create2Callback cb = (Create2Callback) p.cb;</span><br><span class="line">			  Create2Response rsp = (Create2Response) p.response;</span><br><span class="line">			  <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx,</span><br><span class="line">						  (chrootPath == null</span><br><span class="line">								  ? rsp.getPath()</span><br><span class="line">								  : rsp.getPath()</span><br><span class="line">							.substring(chrootPath.length())), rsp.getStat());</span><br><span class="line">			  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, null, null);</span><br><span class="line">			  &#125;                   </span><br><span class="line">		  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p.response instanceof MultiResponse) &#123;</span><br><span class="line">			  MultiCallback cb = (MultiCallback) p.cb;</span><br><span class="line">			  MultiResponse rsp = (MultiResponse) p.response;</span><br><span class="line">			  <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">				  List&lt;OpResult&gt; results = rsp.getResultList();</span><br><span class="line">				  int newRc = rc;</span><br><span class="line">				  <span class="keyword">for</span> (OpResult result : results) &#123;</span><br><span class="line">					  <span class="keyword">if</span> (result instanceof ErrorResult</span><br><span class="line">							  &amp;&amp; KeeperException.Code.OK.intValue() != (newRc = ((ErrorResult) result)</span><br><span class="line">							  .getErr())) &#123;</span><br><span class="line">						  <span class="built_in">break</span>;</span><br><span class="line">					  &#125;</span><br><span class="line">				  &#125;</span><br><span class="line">				  cb.processResult(newRc, clientPath, p.ctx, results);</span><br><span class="line">			  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				  cb.processResult(rc, clientPath, p.ctx, null);</span><br><span class="line">			  &#125;</span><br><span class="line">		  &#125;  <span class="keyword">else</span> <span class="keyword">if</span> (p.cb instanceof VoidCallback) &#123;</span><br><span class="line">			  VoidCallback cb = (VoidCallback) p.cb;</span><br><span class="line">			  cb.processResult(rc, clientPath, p.ctx);</span><br><span class="line">		  &#125;</span><br><span class="line">	  &#125;</span><br><span class="line">  &#125; catch (Throwable t) &#123;</span><br><span class="line">	  LOG.error(<span class="string">"Caught unexpected throwable"</span>, t);</span><br><span class="line">  &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></p>
<p>当在finishPacket执行完notify后，就会唤醒前面的对应函数，像是getData的，会判断返回头信息是否需要抛出异常<br>如果没有异常，则装载stat并调用response的getData获得服务端返回的数据并返回<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);</span><br><span class="line"><span class="keyword">if</span> (r.getErr() != 0) &#123;</span><br><span class="line">	throw KeeperException.create(KeeperException.Code.get(r.getErr()),</span><br><span class="line">			clientPath);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">stat</span> != null) &#123;</span><br><span class="line">	DataTree.copyStat(response.getStat(), <span class="built_in">stat</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">return</span> response.getData();</span><br></pre></td></tr></table></figure></p>
<p>吐槽一下，zk里面使用netty做异步io，源码跳来跳去的，这博客1137行（–）</p>
<h3 id="更多精彩内容"><a href="#更多精彩内容" class="headerlink" title="更多精彩内容"></a>更多精彩内容</h3><h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-zookeeper源码分析-选举算法" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/07/28/zookeeper源码分析-选举算法/" class="article-date">
  	<time datetime="2019-07-28T15:20:50.486Z" itemprop="datePublished">2019-07-28</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/07/28/zookeeper源码分析-选举算法/">
        zookeeper源码分析-选举算法
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第二十五篇博客–zookeeper源码分析-选举算法</p>
<h2 id="入口类"><a href="#入口类" class="headerlink" title="入口类"></a>入口类</h2><p>启动zookeeper后输入java命令jps，查看到线程运行的主类为QuoRumPeerMain<br><img src="/blog/images/zookeeper/jps查看主类.jpg" alt="QuoRumPeerMain"><br>QuoRumPeerMain的启动方法根据启动命令传递的参数，读取配置文件去解析，根据是单机还是集群使用不同的方法运行<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">protected void initializeAndRun(String[] args)</span><br><span class="line">       throws ConfigException, IOException, AdminServerException</span><br><span class="line">   &#123;</span><br><span class="line">       QuorumPeerConfig config = new QuorumPeerConfig();</span><br><span class="line">       <span class="keyword">if</span> (args.length == 1) &#123;</span><br><span class="line">           config.parse(args[0]);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       // Start and schedule the the purge task</span><br><span class="line">       DatadirCleanupManager purgeMgr = new DatadirCleanupManager(config</span><br><span class="line">               .getDataDir(), config.getDataLogDir(), config</span><br><span class="line">               .getSnapRetainCount(), config.getPurgeInterval());</span><br><span class="line">       purgeMgr.start();</span><br><span class="line">	</span><br><span class="line">	//如果是集群模式，使用runFromConfig方法，否则使用ZooKeeperServerMain.main方法运行</span><br><span class="line">       <span class="keyword">if</span> (args.length == 1 &amp;&amp; config.isDistributed()) &#123;</span><br><span class="line">           runFromConfig(config);</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           LOG.warn(<span class="string">"Either no config or no quorum defined in config, running "</span></span><br><span class="line">                   + <span class="string">" in standalone mode"</span>);</span><br><span class="line">           // there is only server <span class="keyword">in</span> the quorum -- run as standalone</span><br><span class="line">           ZooKeeperServerMain.main(args);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="集群运行"><a href="#集群运行" class="headerlink" title="集群运行"></a>集群运行</h2><p>runFromConfig方法根据传递进来的配置信息，绑定成员quorumPeer，加载属性<br>quorumPeer.setQuorumVerifier(config.getQuorumVerifier(), false)这一句代码加载的QuorumVerifier包含了从配置文件解析来的集群中所有成员的信息对象<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line">public void runFromConfig(QuorumPeerConfig config)</span><br><span class="line">           throws IOException, AdminServerException</span><br><span class="line">   &#123;</span><br><span class="line">	......</span><br><span class="line"></span><br><span class="line">         quorumPeer = getQuorumPeer();</span><br><span class="line">         quorumPeer.setTxnFactory(new FileTxnSnapLog(</span><br><span class="line">                     config.getDataLogDir(),</span><br><span class="line">                     config.getDataDir()));</span><br><span class="line">         quorumPeer.enableLocalSessions(config.areLocalSessionsEnabled());</span><br><span class="line">         quorumPeer.enableLocalSessionsUpgrading(</span><br><span class="line">             config.isLocalSessionsUpgradingEnabled());</span><br><span class="line">         //quorumPeer.setQuorumPeers(config.getAllMembers());</span><br><span class="line">         quorumPeer.setElectionType(config.getElectionAlg());</span><br><span class="line">         quorumPeer.setMyid(config.getServerId());</span><br><span class="line">         quorumPeer.setTickTime(config.getTickTime());</span><br><span class="line">         quorumPeer.setMinSessionTimeout(config.getMinSessionTimeout());</span><br><span class="line">         quorumPeer.setMaxSessionTimeout(config.getMaxSessionTimeout());</span><br><span class="line">         quorumPeer.setInitLimit(config.getInitLimit());</span><br><span class="line">         quorumPeer.setSyncLimit(config.getSyncLimit());</span><br><span class="line">         quorumPeer.setConfigFileName(config.getConfigFilename());</span><br><span class="line">         quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory()));</span><br><span class="line">         quorumPeer.setQuorumVerifier(config.getQuorumVerifier(), <span class="literal">false</span>);</span><br><span class="line">         <span class="keyword">if</span> (config.getLastSeenQuorumVerifier()!=null) &#123;</span><br><span class="line">             quorumPeer.setLastSeenQuorumVerifier(config.getLastSeenQuorumVerifier(), <span class="literal">false</span>);</span><br><span class="line">         &#125;</span><br><span class="line">         quorumPeer.initConfigInZKDatabase();</span><br><span class="line">         quorumPeer.setCnxnFactory(cnxnFactory);</span><br><span class="line">         quorumPeer.setSecureCnxnFactory(secureCnxnFactory);</span><br><span class="line">         quorumPeer.setSslQuorum(config.isSslQuorum());</span><br><span class="line">         quorumPeer.setUsePortUnification(config.shouldUsePortUnification());</span><br><span class="line">         quorumPeer.setLearnerType(config.getPeerType());</span><br><span class="line">         quorumPeer.setSyncEnabled(config.getSyncEnabled());</span><br><span class="line">         quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs());</span><br><span class="line">         <span class="keyword">if</span> (config.sslQuorumReloadCertFiles) &#123;</span><br><span class="line">             quorumPeer.getX509Util().enableCertFileReloading();</span><br><span class="line">         &#125;</span><br><span class="line"></span><br><span class="line">         // sets quorum sasl authentication configurations</span><br><span class="line">         quorumPeer.setQuorumSaslEnabled(config.quorumEnableSasl);</span><br><span class="line">         <span class="keyword">if</span>(quorumPeer.isQuorumSaslAuthEnabled())&#123;</span><br><span class="line">             quorumPeer.setQuorumServerSaslRequired(config.quorumServerRequireSasl);</span><br><span class="line">             quorumPeer.setQuorumLearnerSaslRequired(config.quorumLearnerRequireSasl);</span><br><span class="line">             quorumPeer.setQuorumServicePrincipal(config.quorumServicePrincipal);</span><br><span class="line">             quorumPeer.setQuorumServerLoginContext(config.quorumServerLoginContext);</span><br><span class="line">             quorumPeer.setQuorumLearnerLoginContext(config.quorumLearnerLoginContext);</span><br><span class="line">         &#125;</span><br><span class="line">         quorumPeer.setQuorumCnxnThreadsSize(config.quorumCnxnThreadsSize);</span><br><span class="line">         quorumPeer.initialize();</span><br><span class="line">         </span><br><span class="line">         quorumPeer.start();</span><br><span class="line">         quorumPeer.join();</span><br><span class="line">	......</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">	public interface QuorumVerifier &#123;</span><br><span class="line">    long getWeight(long id);</span><br><span class="line">    boolean containsQuorum(Set&lt;Long&gt; <span class="built_in">set</span>);</span><br><span class="line">    long getVersion();</span><br><span class="line">    void setVersion(long ver);</span><br><span class="line">    Map&lt;Long, QuorumServer&gt; getAllMembers();//全部集群成员</span><br><span class="line">    Map&lt;Long, QuorumServer&gt; getVotingMembers();//参与投票成员</span><br><span class="line">    Map&lt;Long, QuorumServer&gt; getObservingMembers();//观察者成员</span><br><span class="line">    boolean equals(Object o);</span><br><span class="line">    String toString();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>接下来调用start方法开始运行QuorumPeer线程成员<br>start方法被重写了，在开始运行前做了一些处理<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">  @Override</span><br><span class="line">  public synchronized void <span class="function"><span class="title">start</span></span>() &#123;</span><br><span class="line">      <span class="keyword">if</span> (!getView().containsKey(myid)) &#123;</span><br><span class="line">	//getView是根据前面配置信息解析设置的QuorumVerifier，获取全部集群机器的id和ip端口信息</span><br><span class="line">	//此处判断当前机器是否在集群里</span><br><span class="line">          throw new RuntimeException(<span class="string">"My id "</span> + myid + <span class="string">" not in the peer list"</span>);</span><br><span class="line">       &#125;</span><br><span class="line">      loadDataBase();//从数据日志文件恢复数据到内存</span><br><span class="line">......</span><br><span class="line">      startLeaderElection();//开启选举</span><br><span class="line">      super.start();//线程类的start方法，开始运行run</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></p>
<p>选举运行，此时刚进入集群节点状态为LOOKING，第一票投票给自己<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">public class QuorumPeerConfig &#123;</span><br><span class="line">    protected int electionAlg = 3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">synchronized public void <span class="function"><span class="title">startLeaderElection</span></span>() &#123;</span><br><span class="line">......</span><br><span class="line">   <span class="keyword">if</span> (getPeerState() == ServerState.LOOKING) &#123;</span><br><span class="line">		//投票给自己</span><br><span class="line">	   currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch());</span><br><span class="line">   &#125;</span><br><span class="line">......</span><br><span class="line">this.electionAlg = createElectionAlgorithm(electionType);//挑选选举算法---配置文件不设置默认为3</span><br><span class="line">&#125;</span><br><span class="line"> protected Election createElectionAlgorithm(int electionAlgorithm)&#123;</span><br><span class="line">       Election le=null;</span><br><span class="line"></span><br><span class="line">       //TODO: use a factory rather than a switch</span><br><span class="line">       switch (electionAlgorithm) &#123;</span><br><span class="line">       <span class="keyword">case</span> 0:</span><br><span class="line">           le = new LeaderElection(this);</span><br><span class="line">           <span class="built_in">break</span>;</span><br><span class="line">       <span class="keyword">case</span> 1:</span><br><span class="line">           le = new AuthFastLeaderElection(this);</span><br><span class="line">           <span class="built_in">break</span>;</span><br><span class="line">       <span class="keyword">case</span> 2:</span><br><span class="line">           le = new AuthFastLeaderElection(this, <span class="literal">true</span>);</span><br><span class="line">           <span class="built_in">break</span>;</span><br><span class="line">       <span class="keyword">case</span> 3:</span><br><span class="line">           QuorumCnxManager qcm = createCnxnManager();</span><br><span class="line">           QuorumCnxManager oldQcm = qcmRef.getAndSet(qcm);</span><br><span class="line">           <span class="keyword">if</span> (oldQcm != null) &#123;</span><br><span class="line">               LOG.warn(<span class="string">"Clobbering already-set QuorumCnxManager (restarting leader election?)"</span>);</span><br><span class="line">               oldQcm.halt();</span><br><span class="line">           &#125;</span><br><span class="line">           QuorumCnxManager.Listener listener = qcm.listener;</span><br><span class="line">           <span class="keyword">if</span>(listener != null)&#123;</span><br><span class="line">               listener.start();</span><br><span class="line">               FastLeaderElection fle = new FastLeaderElection(this, qcm);</span><br><span class="line">               fle.start();</span><br><span class="line">               le = fle;</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">               LOG.error(<span class="string">"Null listener when initializing cnx manager"</span>);</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="built_in">break</span>;</span><br><span class="line">       default:</span><br><span class="line">           assert <span class="literal">false</span>;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="built_in">return</span> le;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<p>zookeeper依赖jmx去实现集群操作，会将自身的运行线程quorumPeer封装为一个QuorumBean，通过MBeanRegistry注册到jmx上<br>MBeanRegistry是zookeeper封装了对MBeanServer的一些操作的一个类，主要完成对objectName的自动生成<br>MBeanRegistry注册时要指定parent，根据parent会自动生成objectName<br>rmi与MBean相关的源码分析请参照本人的另外两篇博客：<br>    1.<a href="https://blog.csdn.net/weixin_44627989/article/details/93055791" target="_blank" rel="noopener">java-rmi源码解析</a><br>    2.<a href="https://blog.csdn.net/weixin_44627989/article/details/95337583" target="_blank" rel="noopener">MBean与JMX</a><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line">public class MBeanRegistry &#123;</span><br><span class="line">    private static final Logger LOG = LoggerFactory.getLogger(MBeanRegistry.class);</span><br><span class="line">    </span><br><span class="line">    private static volatile MBeanRegistry instance = new MBeanRegistry();</span><br><span class="line">    </span><br><span class="line">    private final Object LOCK = new Object();</span><br><span class="line">    </span><br><span class="line">    private Map&lt;ZKMBeanInfo, String&gt; mapBean2Path =</span><br><span class="line">        new ConcurrentHashMap&lt;ZKMBeanInfo, String&gt;();</span><br><span class="line">    </span><br><span class="line">    private MBeanServer mBeanServer;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Useful <span class="keyword">for</span> unit tests. Change the MBeanRegistry instance</span><br><span class="line">     *</span><br><span class="line">     * @param instance new instance</span><br><span class="line">     */</span><br><span class="line">    public static void setInstance(MBeanRegistry instance) &#123;</span><br><span class="line">        MBeanRegistry.instance = instance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static MBeanRegistry <span class="function"><span class="title">getInstance</span></span>() &#123;</span><br><span class="line">        <span class="built_in">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public <span class="function"><span class="title">MBeanRegistry</span></span> () &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            mBeanServer = ManagementFactory.getPlatformMBeanServer();        </span><br><span class="line">        &#125; catch (Error e) &#123;</span><br><span class="line">            // Account <span class="keyword">for</span> running within IKVM and create a new MBeanServer</span><br><span class="line">            // <span class="keyword">if</span> the PlatformMBeanServer does not exist.</span><br><span class="line">            mBeanServer =  MBeanServerFactory.createMBeanServer();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Return the underlying MBeanServer that is being</span><br><span class="line">     * used to register MBean<span class="string">'s. The returned MBeanServer</span></span><br><span class="line"><span class="string">     * may be a new empty MBeanServer if running through IKVM.</span></span><br><span class="line"><span class="string">     */</span></span><br><span class="line"><span class="string">    public MBeanServer getPlatformMBeanServer() &#123;</span></span><br><span class="line"><span class="string">        return mBeanServer;</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    /**</span></span><br><span class="line"><span class="string">     * Registers a new MBean with the platform MBean server. </span></span><br><span class="line"><span class="string">     * @param bean the bean being registered</span></span><br><span class="line"><span class="string">     * @param parent if not null, the new bean will be registered as a child</span></span><br><span class="line"><span class="string">     * node of this parent.</span></span><br><span class="line"><span class="string">     */</span></span><br><span class="line"><span class="string">    public void register(ZKMBeanInfo bean, ZKMBeanInfo parent)</span></span><br><span class="line"><span class="string">        throws JMException</span></span><br><span class="line"><span class="string">    &#123;</span></span><br><span class="line"><span class="string">        assert bean != null;</span></span><br><span class="line"><span class="string">        String path = null;</span></span><br><span class="line"><span class="string">        if (parent != null) &#123;</span></span><br><span class="line"><span class="string">            path = mapBean2Path.get(parent);</span></span><br><span class="line"><span class="string">            assert path != null;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">        path = makeFullPath(path, parent);</span></span><br><span class="line"><span class="string">        if(bean.isHidden())</span></span><br><span class="line"><span class="string">            return;</span></span><br><span class="line"><span class="string">        ObjectName oname = makeObjectName(path, bean);</span></span><br><span class="line"><span class="string">        try &#123;</span></span><br><span class="line"><span class="string">            synchronized (LOCK) &#123;</span></span><br><span class="line"><span class="string">                mBeanServer.registerMBean(bean, oname);</span></span><br><span class="line"><span class="string">                mapBean2Path.put(bean, path);</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">        &#125; catch (JMException e) &#123;</span></span><br><span class="line"><span class="string">            LOG.warn("Failed to register MBean " + bean.getName());</span></span><br><span class="line"><span class="string">            throw e;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">    &#125;</span></span><br></pre></td></tr></table></figure></p>
<p>run方法细节，将本地的jmxQuorumBean注册到jmx，并且根据读取配置文件的集群信息，把每个远程节点的信息封装以后，以本机节点jmxQuorumBean为parent，注册到Jmx上<br>接下来因为状态为欸LOOKING，进入选举方法<br>setCurrentVote(makeLEStrategy().lookForLeader());<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">    public void <span class="function"><span class="title">run</span></span>() &#123;</span><br><span class="line">        updateThreadName();</span><br><span class="line"></span><br><span class="line">            jmxQuorumBean = new QuorumBean(this);</span><br><span class="line">            MBeanRegistry.getInstance().register(jmxQuorumBean, null);</span><br><span class="line">            <span class="keyword">for</span>(QuorumServer s: getView().values())&#123;</span><br><span class="line">                ZKMBeanInfo p;</span><br><span class="line">                <span class="keyword">if</span> (getId() == s.id) &#123;</span><br><span class="line">                    p = jmxLocalPeerBean = new LocalPeerBean(this);</span><br><span class="line">                    try &#123;</span><br><span class="line">                        MBeanRegistry.getInstance().register(p, jmxQuorumBean);</span><br><span class="line">                    &#125; catch (Exception e) &#123;</span><br><span class="line">                        LOG.warn(<span class="string">"Failed to register with JMX"</span>, e);</span><br><span class="line">                        jmxLocalPeerBean = null;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    RemotePeerBean rBean = new RemotePeerBean(this, s);</span><br><span class="line">                    try &#123;</span><br><span class="line">                        MBeanRegistry.getInstance().register(rBean, jmxQuorumBean);</span><br><span class="line">                        jmxRemotePeerBean.put(s.id, rBean);</span><br><span class="line">                    &#125; catch (Exception e) &#123;</span><br><span class="line">                        LOG.warn(<span class="string">"Failed to register with JMX"</span>, e);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            /*</span><br><span class="line">             * Main loop</span><br><span class="line">             */</span><br><span class="line">            <span class="keyword">while</span> (running) &#123;</span><br><span class="line">                switch (getPeerState()) &#123;</span><br><span class="line">                <span class="keyword">case</span> LOOKING:</span><br><span class="line">                    LOG.info(<span class="string">"LOOKING"</span>);</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (Boolean.getBoolean(<span class="string">"readonlymode.enabled"</span>)) &#123;</span><br><span class="line">                        LOG.info(<span class="string">"Attempting to start ReadOnlyZooKeeperServer"</span>);</span><br><span class="line"></span><br><span class="line">                        // Create <span class="built_in">read</span>-only server but don<span class="string">'t start it immediately</span></span><br><span class="line"><span class="string">                        final ReadOnlyZooKeeperServer roZk =</span></span><br><span class="line"><span class="string">                            new ReadOnlyZooKeeperServer(logFactory, this, this.zkDb);</span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">                        // Instead of starting roZk immediately, wait some grace</span></span><br><span class="line"><span class="string">                        // period before we decide we'</span>re partitioned.</span><br><span class="line">                        //</span><br><span class="line">                        // Thread is used here because otherwise it would require</span><br><span class="line">                        // changes <span class="keyword">in</span> each of election strategy classes <span class="built_in">which</span> is</span><br><span class="line">                        // unnecessary code coupling.</span><br><span class="line">                        Thread roZkMgr = new <span class="function"><span class="title">Thread</span></span>() &#123;</span><br><span class="line">                            public void <span class="function"><span class="title">run</span></span>() &#123;</span><br><span class="line">                                try &#123;</span><br><span class="line">                                    // lower-bound grace period to 2 secs</span><br><span class="line">                                    sleep(Math.max(2000, tickTime));</span><br><span class="line">                                    <span class="keyword">if</span> (ServerState.LOOKING.equals(getPeerState())) &#123;</span><br><span class="line">                                        roZk.startup();</span><br><span class="line">                                    &#125;</span><br><span class="line">                                &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                                    LOG.info(<span class="string">"Interrupted while attempting to start ReadOnlyZooKeeperServer, not started"</span>);</span><br><span class="line">                                &#125; catch (Exception e) &#123;</span><br><span class="line">                                    LOG.error(<span class="string">"FAILED to start ReadOnlyZooKeeperServer"</span>, e);</span><br><span class="line">                                &#125;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;;</span><br><span class="line">                        try &#123;</span><br><span class="line">                            roZkMgr.start();</span><br><span class="line">                            reconfigFlagClear();</span><br><span class="line">                            <span class="keyword">if</span> (shuttingDownLE) &#123;</span><br><span class="line">                                shuttingDownLE = <span class="literal">false</span>;</span><br><span class="line">                                startLeaderElection();</span><br><span class="line">                            &#125;</span><br><span class="line">                            setCurrentVote(makeLEStrategy().lookForLeader());</span><br><span class="line">                        &#125; catch (Exception e) &#123;</span><br><span class="line">                            LOG.warn(<span class="string">"Unexpected exception"</span>, e);</span><br><span class="line">                            setPeerState(ServerState.LOOKING);</span><br><span class="line">                        &#125; finally &#123;</span><br><span class="line">                            // If the thread is <span class="keyword">in</span> the the grace period, interrupt</span><br><span class="line">                            // to come out of waiting.</span><br><span class="line">                            roZkMgr.interrupt();</span><br><span class="line">                            roZk.shutdown();</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        try &#123;</span><br><span class="line">                           reconfigFlagClear();</span><br><span class="line">                            <span class="keyword">if</span> (shuttingDownLE) &#123;</span><br><span class="line">                               shuttingDownLE = <span class="literal">false</span>;</span><br><span class="line">                               startLeaderElection();</span><br><span class="line">                               &#125;</span><br><span class="line">                            setCurrentVote(makeLEStrategy().lookForLeader());</span><br><span class="line">                        &#125; catch (Exception e) &#123;</span><br><span class="line">                            LOG.warn(<span class="string">"Unexpected exception"</span>, e);</span><br><span class="line">                            setPeerState(ServerState.LOOKING);</span><br><span class="line">                        &#125;                        </span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="built_in">break</span>;</span><br><span class="line">			.....</span><br><span class="line">            &#125;</span><br><span class="line">		......</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>根据前面分析得知，使用的默认选举策略为FastLeaderElection<br>FastLeaderElection构造时会实例化一个Messager<br>Messenger实例化时会创建两个线程，一个负责接收消息，一个负责发送消息启动两个线程，一个负责接收消息，一个负责发送消息<br>而FastLeaderElection对象前面挑选选举算法时，构建完成就会调用start方法，对应会启动两个工作线程开始接收和发送消息<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">   public FastLeaderElection(QuorumPeer self, QuorumCnxManager manager)&#123;</span><br><span class="line">       this.stop = <span class="literal">false</span>;</span><br><span class="line">       this.manager = manager;</span><br><span class="line">       starter(self, manager);</span><br><span class="line">   &#125;</span><br><span class="line">   LinkedBlockingQueue&lt;ToSend&gt; sendqueue;</span><br><span class="line">   LinkedBlockingQueue&lt;Notification&gt; recvqueue;</span><br><span class="line">   /**</span><br><span class="line">    * This method is invoked by the constructor. Because it is a</span><br><span class="line">    * part of the starting procedure of the object that must be on</span><br><span class="line">    * any constructor of this class, it is probably best to keep as</span><br><span class="line">    * a separate method. As we have a single constructor currently,</span><br><span class="line">    * it is not strictly necessary to have it separate.</span><br><span class="line">    *</span><br><span class="line">    * @param self      QuorumPeer that created this object</span><br><span class="line">    * @param manager   Connection manager</span><br><span class="line">    */</span><br><span class="line">   private void starter(QuorumPeer self, QuorumCnxManager manager) &#123;</span><br><span class="line">       this.self = self;</span><br><span class="line">       proposedLeader = -1;</span><br><span class="line">       proposedZxid = -1;</span><br><span class="line"></span><br><span class="line">       sendqueue = new LinkedBlockingQueue&lt;ToSend&gt;();</span><br><span class="line">       recvqueue = new LinkedBlockingQueue&lt;Notification&gt;();</span><br><span class="line">       this.messenger = new Messenger(manager);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">Messenger(QuorumCnxManager manager) &#123;</span><br><span class="line"></span><br><span class="line">	this.ws = new WorkerSender(manager);</span><br><span class="line"></span><br><span class="line">	this.wsThread = new Thread(this.ws,</span><br><span class="line">			<span class="string">"WorkerSender[myid="</span> + self.getId() + <span class="string">"]"</span>);</span><br><span class="line">	this.wsThread.setDaemon(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">	this.wr = new WorkerReceiver(manager);</span><br><span class="line"></span><br><span class="line">	this.wrThread = new Thread(this.wr,</span><br><span class="line">			<span class="string">"WorkerReceiver[myid="</span> + self.getId() + <span class="string">"]"</span>);</span><br><span class="line">	this.wrThread.setDaemon(<span class="literal">true</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * Starts instances of WorkerSender and WorkerReceiver</span><br><span class="line"> */</span><br><span class="line">void <span class="function"><span class="title">start</span></span>()&#123;</span><br><span class="line">	this.wsThread.start();</span><br><span class="line">	this.wrThread.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>该策略为搜寻收到的消息，如果收到的消息的epoch大于当前epoch（朝代），更新本地保存的朝代，直接清空所有的接受消息，对比消息投票属性和自己的初始化属性，如果对方胜出，则将投票改为选举对方挑选的leader，否则投票给自己（初始化时的id-myid）<br>以下为对比方法（epoch都是peerEpoch)<br>((newEpoch &gt; curEpoch) ||<br>                ((newEpoch == curEpoch) &amp;&amp;<br>                ((newZxid &gt; curZxid) || ((newZxid == curZxid) &amp;&amp; (newId &gt; curId)))))<br>胜出条件:<br>    1.朝代大于当前朝代<br>    2.朝代相等，并且zxid大的 或者 （zxid相等并且id更大的）———-因为zxid代表事务id，更大相当于对方处理了更多的leader消息<br>当没有新的投票消息时，则继续发送自己的投票消息（sendNotification方法实际只是将消息加入阻塞队列，真正进行发送处理的是另一个WorkerSender线程子类实例，该线程循环获取阻塞队列的消息，进行发送，对应的接受消息是WorkerReader线程子类实例）<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br></pre></td><td class="code"><pre><span class="line">public Vote lookForLeader() throws InterruptedException &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            self.jmxLeaderElectionBean = new LeaderElectionBean();</span><br><span class="line">            MBeanRegistry.getInstance().register(</span><br><span class="line">                    self.jmxLeaderElectionBean, self.jmxLocalPeerBean);</span><br><span class="line">        &#125; catch (Exception e) &#123;</span><br><span class="line">            LOG.warn(<span class="string">"Failed to register with JMX"</span>, e);</span><br><span class="line">            self.jmxLeaderElectionBean = null;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (self.start_fle == 0) &#123;</span><br><span class="line">           self.start_fle = Time.currentElapsedTime();</span><br><span class="line">        &#125;</span><br><span class="line">        try &#123;</span><br><span class="line">            HashMap&lt;Long, Vote&gt; recvset = new HashMap&lt;Long, Vote&gt;();</span><br><span class="line"></span><br><span class="line">            HashMap&lt;Long, Vote&gt; outofelection = new HashMap&lt;Long, Vote&gt;();</span><br><span class="line"></span><br><span class="line">            int notTimeout = finalizeWait;</span><br><span class="line"></span><br><span class="line">            synchronized(this)&#123;</span><br><span class="line">                logicalclock.incrementAndGet();</span><br><span class="line">                updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            LOG.info(<span class="string">"New election. My id =  "</span> + self.getId() +</span><br><span class="line">                    <span class="string">", proposed zxid=0x"</span> + Long.toHexString(proposedZxid));</span><br><span class="line">            sendNotifications();</span><br><span class="line"></span><br><span class="line">            /*</span><br><span class="line">             * Loop <span class="keyword">in</span> <span class="built_in">which</span> we exchange notifications until we find a leader</span><br><span class="line">             */</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span> ((self.getPeerState() == ServerState.LOOKING) &amp;&amp;</span><br><span class="line">                    (!stop))&#123;</span><br><span class="line">                /*</span><br><span class="line">                 * Remove next notification from queue, <span class="built_in">times</span> out after 2 <span class="built_in">times</span></span><br><span class="line">                 * the termination time</span><br><span class="line">                 */</span><br><span class="line">                Notification n = recvqueue.poll(notTimeout,</span><br><span class="line">                        TimeUnit.MILLISECONDS);</span><br><span class="line"></span><br><span class="line">                /*</span><br><span class="line">                 * Sends more notifications <span class="keyword">if</span> haven<span class="string">'t received enough.</span></span><br><span class="line"><span class="string">                 * Otherwise processes new notification.</span></span><br><span class="line"><span class="string">                 */</span></span><br><span class="line"><span class="string">                if(n == null)&#123;</span></span><br><span class="line"><span class="string">                    if(manager.haveDelivered())&#123;</span></span><br><span class="line"><span class="string">						//如果全部发送完了，再次发送一次投票信息</span></span><br><span class="line"><span class="string">                        sendNotifications();</span></span><br><span class="line"><span class="string">                    &#125; else &#123;</span></span><br><span class="line"><span class="string">						//没发送完，可能出现故障，重新连接每一台集群中的机器</span></span><br><span class="line"><span class="string">                        manager.connectAll();</span></span><br><span class="line"><span class="string">                    &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                    /*</span></span><br><span class="line"><span class="string">                     * Exponential backoff</span></span><br><span class="line"><span class="string">                     */</span></span><br><span class="line"><span class="string">                    int tmpTimeOut = notTimeout*2;</span></span><br><span class="line"><span class="string">                    notTimeout = (tmpTimeOut &lt; maxNotificationInterval?</span></span><br><span class="line"><span class="string">                            tmpTimeOut : maxNotificationInterval);</span></span><br><span class="line"><span class="string">                    LOG.info("Notification time out: " + notTimeout);</span></span><br><span class="line"><span class="string">                &#125; </span></span><br><span class="line"><span class="string">                else if (validVoter(n.sid) &amp;&amp; validVoter(n.leader)) &#123;</span></span><br><span class="line"><span class="string">                    /*</span></span><br><span class="line"><span class="string">                     * Only proceed if the vote comes from a replica in the current or next</span></span><br><span class="line"><span class="string">                     * voting view for a replica in the current or next voting view.</span></span><br><span class="line"><span class="string">                     */</span></span><br><span class="line"><span class="string">                    switch (n.state) &#123;</span></span><br><span class="line"><span class="string">                    case LOOKING:</span></span><br><span class="line"><span class="string">                        // If notification &gt; current, replace and send messages out</span></span><br><span class="line"><span class="string">                        if (n.electionEpoch &gt; logicalclock.get()) &#123;</span></span><br><span class="line"><span class="string">                            logicalclock.set(n.electionEpoch);</span></span><br><span class="line"><span class="string">                            recvset.clear();</span></span><br><span class="line"><span class="string">                            if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,</span></span><br><span class="line"><span class="string">                                    getInitId(), getInitLastLoggedZxid(), getPeerEpoch())) &#123;</span></span><br><span class="line"><span class="string">                                updateProposal(n.leader, n.zxid, n.peerEpoch);</span></span><br><span class="line"><span class="string">                            &#125; else &#123;</span></span><br><span class="line"><span class="string">                                updateProposal(getInitId(),</span></span><br><span class="line"><span class="string">                                        getInitLastLoggedZxid(),</span></span><br><span class="line"><span class="string">                                        getPeerEpoch());</span></span><br><span class="line"><span class="string">                            &#125;</span></span><br><span class="line"><span class="string">                            sendNotifications();</span></span><br><span class="line"><span class="string">                        &#125; else if (n.electionEpoch &lt; logicalclock.get()) &#123;</span></span><br><span class="line"><span class="string">                            if(LOG.isDebugEnabled())&#123;</span></span><br><span class="line"><span class="string">                                LOG.debug("Notification election epoch is smaller than logicalclock. n.electionEpoch = 0x"</span></span><br><span class="line"><span class="string">                                        + Long.toHexString(n.electionEpoch)</span></span><br><span class="line"><span class="string">                                        + ", logicalclock=0x" + Long.toHexString(logicalclock.get()));</span></span><br><span class="line"><span class="string">                            &#125;</span></span><br><span class="line"><span class="string">                            break;</span></span><br><span class="line"><span class="string">                        &#125; else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,</span></span><br><span class="line"><span class="string">                                proposedLeader, proposedZxid, proposedEpoch)) &#123;</span></span><br><span class="line"><span class="string">                            updateProposal(n.leader, n.zxid, n.peerEpoch);</span></span><br><span class="line"><span class="string">                            sendNotifications();</span></span><br><span class="line"><span class="string">                        &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                        if(LOG.isDebugEnabled())&#123;</span></span><br><span class="line"><span class="string">                            LOG.debug("Adding vote: from=" + n.sid +</span></span><br><span class="line"><span class="string">                                    ", proposed leader=" + n.leader +</span></span><br><span class="line"><span class="string">                                    ", proposed zxid=0x" + Long.toHexString(n.zxid) +</span></span><br><span class="line"><span class="string">                                    ", proposed election epoch=0x" + Long.toHexString(n.electionEpoch));</span></span><br><span class="line"><span class="string">                        &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                        // don'</span>t care about the version <span class="keyword">if</span> it<span class="string">'s in LOOKING state</span></span><br><span class="line"><span class="string">                        recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                        if (termPredicate(recvset,</span></span><br><span class="line"><span class="string">                                new Vote(proposedLeader, proposedZxid,</span></span><br><span class="line"><span class="string">                                        logicalclock.get(), proposedEpoch))) &#123;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                            // Verify if there is any change in the proposed leader</span></span><br><span class="line"><span class="string">                            while((n = recvqueue.poll(finalizeWait,</span></span><br><span class="line"><span class="string">                                    TimeUnit.MILLISECONDS)) != null)&#123;</span></span><br><span class="line"><span class="string">                                if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,</span></span><br><span class="line"><span class="string">                                        proposedLeader, proposedZxid, proposedEpoch))&#123;</span></span><br><span class="line"><span class="string">                                    recvqueue.put(n);</span></span><br><span class="line"><span class="string">                                    break;</span></span><br><span class="line"><span class="string">                                &#125;</span></span><br><span class="line"><span class="string">                            &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">                            /*</span></span><br><span class="line"><span class="string">                             * This predicate is true once we don'</span>t <span class="built_in">read</span> any new</span><br><span class="line">                             * relevant message from the reception queue</span><br><span class="line">                             */</span><br><span class="line">                            <span class="keyword">if</span> (n == null) &#123;</span><br><span class="line">                                self.setPeerState((proposedLeader == self.getId()) ?</span><br><span class="line">                                        ServerState.LEADING: learningState());</span><br><span class="line">                                Vote endVote = new Vote(proposedLeader,</span><br><span class="line">                                        proposedZxid, logicalclock.get(), </span><br><span class="line">                                        proposedEpoch);</span><br><span class="line">                                leaveInstance(endVote);</span><br><span class="line">                                <span class="built_in">return</span> endVote;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="built_in">break</span>;</span><br><span class="line">						......</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (!validVoter(n.leader)) &#123;</span><br><span class="line">                        LOG.warn(<span class="string">"Ignoring notification for non-cluster member sid &#123;&#125; from sid &#123;&#125;"</span>, n.leader, n.sid);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> (!validVoter(n.sid)) &#123;</span><br><span class="line">                        LOG.warn(<span class="string">"Ignoring notification for sid &#123;&#125; from non-quorum member sid &#123;&#125;"</span>, n.leader, n.sid);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">return</span> null;</span><br><span class="line">        &#125; finally &#123;</span><br><span class="line">            try &#123;</span><br><span class="line">                <span class="keyword">if</span>(self.jmxLeaderElectionBean != null)&#123;</span><br><span class="line">                    MBeanRegistry.getInstance().unregister(</span><br><span class="line">                            self.jmxLeaderElectionBean);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; catch (Exception e) &#123;</span><br><span class="line">                LOG.warn(<span class="string">"Failed to unregister with JMX"</span>, e);</span><br><span class="line">            &#125;</span><br><span class="line">            self.jmxLeaderElectionBean = null;</span><br><span class="line">            LOG.debug(<span class="string">"Number of connection processing threads: &#123;&#125;"</span>,</span><br><span class="line">                    manager.getConnectionThreadCount());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>投票以后会进入选举是否完成的判断,如果确定选举有了结果就会设置自身的状态并结束选举<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (termPredicate(recvset,</span><br><span class="line">		new Vote(proposedLeader, proposedZxid,</span><br><span class="line">				logicalclock.get(), proposedEpoch))) &#123;</span><br><span class="line"></span><br><span class="line">	// Verify <span class="keyword">if</span> there is any change <span class="keyword">in</span> the proposed leader</span><br><span class="line">	<span class="keyword">while</span>((n = recvqueue.poll(finalizeWait,</span><br><span class="line">			TimeUnit.MILLISECONDS)) != null)&#123;</span><br><span class="line">		<span class="keyword">if</span>(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,</span><br><span class="line">				proposedLeader, proposedZxid, proposedEpoch))&#123;</span><br><span class="line">			recvqueue.put(n);</span><br><span class="line">			<span class="built_in">break</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	/*</span><br><span class="line">	 * This predicate is <span class="literal">true</span> once we don<span class="string">'t read any new</span></span><br><span class="line"><span class="string">	 * relevant message from the reception queue</span></span><br><span class="line"><span class="string">	 */</span></span><br><span class="line"><span class="string">	if (n == null) &#123;</span></span><br><span class="line"><span class="string">		self.setPeerState((proposedLeader == self.getId()) ?</span></span><br><span class="line"><span class="string">				ServerState.LEADING: learningState());</span></span><br><span class="line"><span class="string">		Vote endVote = new Vote(proposedLeader,</span></span><br><span class="line"><span class="string">				proposedZxid, logicalclock.get(), </span></span><br><span class="line"><span class="string">				proposedEpoch);</span></span><br><span class="line"><span class="string">		leaveInstance(endVote);</span></span><br><span class="line"><span class="string">		return endVote;</span></span><br><span class="line"><span class="string">	&#125;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">protected boolean termPredicate(Map&lt;Long, Vote&gt; votes, Vote vote) &#123;</span></span><br><span class="line"><span class="string">       SyncedLearnerTracker voteSet = new SyncedLearnerTracker();</span></span><br><span class="line"><span class="string">       voteSet.addQuorumVerifier(self.getQuorumVerifier());</span></span><br><span class="line"><span class="string">       if (self.getLastSeenQuorumVerifier() != null</span></span><br><span class="line"><span class="string">               &amp;&amp; self.getLastSeenQuorumVerifier().getVersion() &gt; self</span></span><br><span class="line"><span class="string">                       .getQuorumVerifier().getVersion()) &#123;</span></span><br><span class="line"><span class="string">           voteSet.addQuorumVerifier(self.getLastSeenQuorumVerifier());</span></span><br><span class="line"><span class="string">       &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">       /*</span></span><br><span class="line"><span class="string">        * First make the views consistent. Sometimes peers will have different</span></span><br><span class="line"><span class="string">        * zxids for a server depending on timing.</span></span><br><span class="line"><span class="string">        */</span></span><br><span class="line"><span class="string">       for (Map.Entry&lt;Long, Vote&gt; entry : votes.entrySet()) &#123;</span></span><br><span class="line"><span class="string">           if (vote.equals(entry.getValue())) &#123;</span></span><br><span class="line"><span class="string">               voteSet.addAck(entry.getKey());</span></span><br><span class="line"><span class="string">           &#125;</span></span><br><span class="line"><span class="string">       &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">       return voteSet.hasAllQuorums();</span></span><br><span class="line"><span class="string">   &#125;</span></span><br></pre></td></tr></table></figure></p>
<p>SyncedLearnerTracker中投票结果的判定是这样实现的<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">public boolean <span class="function"><span class="title">hasAllQuorums</span></span>() &#123;</span><br><span class="line">    <span class="keyword">for</span> (QuorumVerifierAcksetPair qvAckset : qvAcksetPairs) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!qvAckset.getQuorumVerifier().containsQuorum(qvAckset.getAckset()))</span><br><span class="line">            <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>而 在QuorumPeerConfig加载配置文件解析时就实例化，根据使用带权重的还是不带权重，分别使用QuorumHierarchical和QuorumMaj<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line">   void setupQuorumPeerConfig(Properties prop, boolean configBackwardCompatibilityMode)</span><br><span class="line">           throws IOException, ConfigException &#123;</span><br><span class="line">       quorumVerifier = parseDynamicConfig(prop, electionAlg, <span class="literal">true</span>, configBackwardCompatibilityMode);</span><br><span class="line">       setupMyId();</span><br><span class="line">       setupClientPort();</span><br><span class="line">       setupPeerType();</span><br><span class="line">       checkValidity();</span><br><span class="line">   &#125;</span><br><span class="line">public static QuorumVerifier parseDynamicConfig(Properties dynamicConfigProp, int eAlg, boolean warnings,</span><br><span class="line">   boolean configBackwardCompatibilityMode) throws IOException, ConfigException &#123;</span><br><span class="line">      boolean isHierarchical = <span class="literal">false</span>;</span><br><span class="line">       <span class="keyword">for</span> (Entry&lt;Object, Object&gt; entry : dynamicConfigProp.entrySet()) &#123;</span><br><span class="line">           String key = entry.getKey().toString().trim();                    </span><br><span class="line">           <span class="keyword">if</span> (key.startsWith(<span class="string">"group"</span>) || key.startsWith(<span class="string">"weight"</span>)) &#123;</span><br><span class="line">              isHierarchical = <span class="literal">true</span>;</span><br><span class="line">           &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!configBackwardCompatibilityMode &amp;&amp; !key.startsWith(<span class="string">"server."</span>) &amp;&amp; !key.equals(<span class="string">"version"</span>))&#123; </span><br><span class="line">              LOG.info(dynamicConfigProp.toString());</span><br><span class="line">              throw new ConfigException(<span class="string">"Unrecognised parameter: "</span> + key);                </span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">       </span><br><span class="line">       QuorumVerifier qv = createQuorumVerifier(dynamicConfigProp, isHierarchical);</span><br><span class="line">              </span><br><span class="line">       int numParticipators = qv.getVotingMembers().size();</span><br><span class="line">       int numObservers = qv.getObservingMembers().size();</span><br><span class="line">       <span class="keyword">if</span> (numParticipators == 0) &#123;</span><br><span class="line">           <span class="keyword">if</span> (!standaloneEnabled) &#123;</span><br><span class="line">               throw new IllegalArgumentException(<span class="string">"standaloneEnabled = false then "</span> +</span><br><span class="line">                       <span class="string">"number of participants should be &gt;0"</span>);</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="keyword">if</span> (numObservers &gt; 0) &#123;</span><br><span class="line">               throw new IllegalArgumentException(<span class="string">"Observers w/o participants is an invalid configuration"</span>);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125; <span class="keyword">else</span> <span class="keyword">if</span> (numParticipators == 1 &amp;&amp; standaloneEnabled) &#123;</span><br><span class="line">           // HBase currently adds a single server line to the config, <span class="keyword">for</span></span><br><span class="line">           // b/w compatibility reasons we need to keep this here. If standaloneEnabled</span><br><span class="line">           // is <span class="literal">true</span>, the QuorumPeerMain script will create a standalone server instead</span><br><span class="line">           // of a quorum configuration</span><br><span class="line">           LOG.error(<span class="string">"Invalid configuration, only one server specified (ignoring)"</span>);</span><br><span class="line">           <span class="keyword">if</span> (numObservers &gt; 0) &#123;</span><br><span class="line">               throw new IllegalArgumentException(<span class="string">"Observers w/o quorum is an invalid configuration"</span>);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           <span class="keyword">if</span> (warnings) &#123;</span><br><span class="line">               <span class="keyword">if</span> (numParticipators &lt;= 2) &#123;</span><br><span class="line">                   LOG.warn(<span class="string">"No server failure will be tolerated. "</span> +</span><br><span class="line">                       <span class="string">"You need at least 3 servers."</span>);</span><br><span class="line">               &#125; <span class="keyword">else</span> <span class="keyword">if</span> (numParticipators % 2 == 0) &#123;</span><br><span class="line">                   LOG.warn(<span class="string">"Non-optimial configuration, consider an odd number of servers."</span>);</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line">           /*</span><br><span class="line">            * If using FLE, <span class="keyword">then</span> every server requires a separate election</span><br><span class="line">            * port.</span><br><span class="line">            */            </span><br><span class="line">          <span class="keyword">if</span> (eAlg != 0) &#123;</span><br><span class="line">              <span class="keyword">for</span> (QuorumServer s : qv.getVotingMembers().values()) &#123;</span><br><span class="line">                  <span class="keyword">if</span> (s.electionAddr == null)</span><br><span class="line">                      throw new IllegalArgumentException(</span><br><span class="line">                              <span class="string">"Missing election port for server: "</span> + s.id);</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;   </span><br><span class="line">       &#125;</span><br><span class="line">       <span class="built_in">return</span> qv;</span><br><span class="line">   &#125;</span><br><span class="line">private static QuorumVerifier createQuorumVerifier(Properties dynamicConfigProp, boolean isHierarchical) throws ConfigException&#123;</span><br><span class="line">      <span class="keyword">if</span>(isHierarchical)&#123;</span><br><span class="line">           <span class="built_in">return</span> new QuorumHierarchical(dynamicConfigProp);</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          /*</span><br><span class="line">            * The default QuorumVerifier is QuorumMaj</span><br><span class="line">            */        </span><br><span class="line">           //LOG.info(<span class="string">"Defaulting to majority quorums"</span>);</span><br><span class="line">           <span class="built_in">return</span> new QuorumMaj(dynamicConfigProp);            </span><br><span class="line">       &#125;          </span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>简单分析QuorumMaj，其判断作为leader的条件就是得票数超过一半<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">public QuorumMaj(Properties props) throws ConfigException &#123;</span><br><span class="line">    <span class="keyword">for</span> (Entry&lt;Object, Object&gt; entry : props.entrySet()) &#123;</span><br><span class="line">        String key = entry.getKey().toString();</span><br><span class="line">        String value = entry.getValue().toString();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (key.startsWith(<span class="string">"server."</span>)) &#123;</span><br><span class="line">            int dot = key.indexOf(<span class="string">'.'</span>);</span><br><span class="line">            long sid = Long.parseLong(key.substring(dot + 1));</span><br><span class="line">            QuorumServer qs = new QuorumServer(sid, value);</span><br><span class="line">            allMembers.put(Long.valueOf(sid), qs);</span><br><span class="line">            <span class="keyword">if</span> (qs.type == LearnerType.PARTICIPANT)</span><br><span class="line">                votingMembers.put(Long.valueOf(sid), qs);</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                observingMembers.put(Long.valueOf(sid), qs);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (key.equals(<span class="string">"version"</span>)) &#123;</span><br><span class="line">            version = Long.parseLong(value, 16);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    half = votingMembers.size() / 2;</span><br><span class="line">&#125;</span><br><span class="line">public boolean containsQuorum(Set&lt;Long&gt; ackSet) &#123;</span><br><span class="line">    <span class="built_in">return</span> (ackSet.size() &gt; half);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="更多精彩内容"><a href="#更多精彩内容" class="headerlink" title="更多精彩内容"></a>更多精彩内容</h3><h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-zookeeper协调原理" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/07/19/zookeeper协调原理/" class="article-date">
  	<time datetime="2019-07-19T10:54:22.951Z" itemprop="datePublished">2019-07-19</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/07/19/zookeeper协调原理/">
        zookeeper协调原理
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第二十四篇博客–zookeeper协调原理</p>
<h2 id="CAP原理"><a href="#CAP原理" class="headerlink" title="CAP原理"></a>CAP原理</h2><p>CAP原则又称CAP定理，指的是在一个分布式系统中，Consistency（一致性）、 Availability（可用性）、Partition tolerance（分区容错性），三者不可兼得。<br>一致性表示分布式系统中各个节点数据的一致性<br>可用性代表数据访问的高性能<br>分区容错性指的是因为同步的时间问题，数据不一致导致出现了多个不同数据版本的分区现象，但系统仍能继续正常运行（容错）<br>很显然，三者最多只能取其二<br>分区容错性与一致性共存（同步需要阻塞）必会与可用性冲突<br>分区容错性与可用性共存（数据不同步）必会与一致性共存冲突<br>可用性与一致性共存必会与分区容错性冲突（实际上这个是不实际的需求，因为分布式环境下因为网络通信的延迟分区容错性是必要的）<br>综上，大部分分布式架构都是实现数据的最终一致性而非实现强一致性（因为分区容错性的必然存在）<br>zookeeper的zap协议就是对2pc进一步提高分区容错性与可用性而降低强一致性的一种协议，同时其保证最终一致性，所以在分布式环境下仍是可用的</p>
<h2 id="事务请求"><a href="#事务请求" class="headerlink" title="事务请求"></a>事务请求</h2><p>所有事务请求（增删改）都会转发到leader由leader处理<br>zookeeper内部是依赖一个改进版的2pc——ZAB协议</p>
<h2 id="传统2pc"><a href="#传统2pc" class="headerlink" title="传统2pc"></a>传统2pc</h2><p>传统2pc分为两个阶段preCommit和commit阶段<br>preCommit阶段由leader向所有节点发送事务请求，各自执行后向leader返回执行成功或者失败，但不进行commit操作（锁资源，将undo和redo信息写到事务日志）<br>commit阶段可由以下几种情况触发:<br>1.超时没有收到所有节点反馈<br>2.某个节点反馈失败<br>3.全部节点返回成功<br>到了commit阶段，1和2两种情况都会触发rollback，leader向参与本次事务的所有节点发送rollback命令，每个节点读取undo，执行事务回滚<br>第3种情况，leader向所有参与本次事务的节点发送commit命令，每个节点读取redo，执行事务提交<br>commit阶段以后，每个节点才会释放资源，切这个过程是同步的，且单点影响较大，单个节点故障会导致集群事务失败</p>
<h2 id="ZAB协议-事务广播"><a href="#ZAB协议-事务广播" class="headerlink" title="ZAB协议-事务广播"></a>ZAB协议-事务广播</h2><p>ZAB协议是一个中心化的分布式协调协议（非中心化的如redis的gossip——原理和网络协议的泛洪法类似）<br>ZAB协议对于事务请求，采取过半选举原则，对于分区容错性进行宽容但避免了单点问题，因为最终会进行数据同步，所以还是能保证分布式环境下数据的最终一致性<br>preCommit阶段，zookeeper的leader节点收到follower发送的事务请求，执行该事务不提交并封装为PROPOSAL消息（包含redo、undo等），向所有follower节点下发该PROPOSAL消息<br>follower节点收到PROPOSAL消息后，将对应的数据信息写到磁盘日志文件返回ack，如果写入失败则抛弃leader，重新加入集群（会等到事务结束才能成功）进入数据同步阶段<br>commit阶段，当超时时间到来前，leader节点收到参与投票的节点（follower节点+leader）超过半数以上的ack时，认为本次事务成功，向所有follower节点广播commit消息（只有zxid），并向observer发送INFORM消息（包含PROPOSAL消息），要求每个节点将PROPOSAL消息执行事务提交（写入内存）<br>如果超时没有收到半数以上的commit，认为本次事务失败，向所有follower节点广播rollback消息，因为提交事务了，执行了该操作的所有zookeeper服务器的zxid会+1<br>接下来，集群所有节点在与leader通信时，发现zxid比leader小的（重新加入集群那部分），leader就会发送命令进行数据同步</p>
<h2 id="ZAB协议-崩溃恢复"><a href="#ZAB协议-崩溃恢复" class="headerlink" title="ZAB协议-崩溃恢复"></a>ZAB协议-崩溃恢复</h2><p>zookeeper集群下，非leader节点出现崩溃只要不影响选举，因为重新启动以后连接集群都要与leader通信，比较zxid进行数据同步，较好解决<br>而leader节点如果崩溃，一般要面临重新选举，选举也是采用半数通过原则（每次选举都会将当前结点能收到消息的，收到最多投票的节点作为下一次选举对象，如果选举对象没有获得半数以上选票会重复这个过程）<br>leader选举后，就面临数据同步问题<br>集群内的有两种<br>1.旧leader收到的事务请求但还没有发出的<br>2.旧leader发出commit操作但是还没有收到全部反馈的<br>对于第一种情况，zookeeper采取的策略是直接丢弃，对执行了该事务未提交的机器进行rollback<br>实现原理基于zxid的设计，zxid是64位，高32位为epoch，低32位为PROPOSAL事务计数器<br>epoch为leader编号，每换一次leader会+1<br>对于第二种情况，zookeeper采取的策略是保证全部提交，这时如果有follower中保存有盖zxid的PROPOSAL事务而未提交，leader会发送commit命令</p>
<h3 id="更多精彩内容"><a href="#更多精彩内容" class="headerlink" title="更多精彩内容"></a>更多精彩内容</h3><h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-zookeeper集群搭建" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/07/11/zookeeper集群搭建/" class="article-date">
  	<time datetime="2019-07-11T01:32:12.876Z" itemprop="datePublished">2019-07-11</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/07/11/zookeeper集群搭建/">
        zookeeper集群搭建
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第二十三篇博客–zookeeper集群搭建</p>
<h2 id="zookeeper简介"><a href="#zookeeper简介" class="headerlink" title="zookeeper简介"></a>zookeeper简介</h2><p>ZooKeeper是一个分布式的，开放源码的分布式应用程序协调服务。<br>它是一个为分布式应用提供一致性服务的软件，提供的功能包括：配置维护、域名服务、分布式同步、组服务等。<br>一开始是用于分布式系统下资源的同步访问<br>在分布式事务场景下也多作为 协调者<br>是一个中心化管理的分布式同步技术<br>zk上的数据和redis类似，存在于内存，保存快照、数据日志等文件</p>
<h2 id="安装zookeeper"><a href="#安装zookeeper" class="headerlink" title="安装zookeeper"></a>安装zookeeper</h2><p>需要安装jdk环境——jdk1.8的u211以上版本<br>下载zookeeper压缩包并解压——3.5.5</p>
<p>复制zoo.cfg.sample为zoo1.cfg作为第一台的配置文件<br>复制多个配置文件作为多台zk启动的不同配置<br>编辑不同配置文件，使用不同客户端连接端口、数据同步端口和选举端口<br>server.1=127.0.0.1:2881:3881——第一个端口代表数据同步端口，第二个代表选举端口<br>dataDir配置到不同目录，不同服务器存放数据的快照文件到不同文件夹，还要存放不同的myid文件<br>myid文件中只保存一个数字代表当前服务器的id<br>dataLogDir配置到不同目录，使得数据的日志分开存储（不包括运行时的日志，运行时日志在log4j配置文件里配置）<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">dataDir=/tmp/zookeeper01</span><br><span class="line">clientPort=2181</span><br><span class="line">server.1=127.0.0.1:2881:3881</span><br><span class="line">server.2=127.0.0.1:2882:3882</span><br><span class="line">server.3=127.0.0.1:2883:3883</span><br></pre></td></tr></table></figure></p>
<h2 id="使用mvn打包"><a href="#使用mvn打包" class="headerlink" title="使用mvn打包"></a>使用mvn打包</h2><p>3.5.5版本的zookeeper需要最少jdk1.8 u211版本，并且默认是没有编译class文件的<br>需要进入zookeeper-server目录，使用maven命令<br>mvn package -Dmaventestskip=true<br>跳过测试程序并打包编译</p>
<h2 id="运行zk集群"><a href="#运行zk集群" class="headerlink" title="运行zk集群"></a>运行zk集群</h2><p>我们配置有三台zk，假设第一个的配置文件为conf目录下的zoo1.cfg，依次类推<br>运行过程为<br>使用zk的bin下的zkServer.sh命令运行<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./zkServer.sh start conf/zoo1.cfg</span><br></pre></td></tr></table></figure></p>
<p>启动成功后，这时使用命令查看状态<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./zkServer.sh start conf/zoo1.cfg</span><br></pre></td></tr></table></figure></p>
<p>应该是提示有错误，可能没有运行成功的<br>那是因为集群里只有一台机器无法选举（因为zk是中心化的，必须有一个协调者），不用管继续启动剩下的机器<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./zkServer.sh start conf/zoo2.cfg</span><br></pre></td></tr></table></figure></p>
<p>这时查看状态会发现两台机器都启动成功了<br>并且一台成为了follower一台成为leader<br>zookeeper集群下的机器有三种角色，leader、follower和observer<br>leader负责事务请求（增删改），任意节点都可处理查询请求，follower参与选举，observer不参与选举只做数据同步<br>接着启动最后一台<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./zkServer.sh start conf/zoo3.cfg</span><br></pre></td></tr></table></figure></p>
<h2 id="开启会话"><a href="#开启会话" class="headerlink" title="开启会话"></a>开启会话</h2><p>使用zkCli命令<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh bin/zkCli.sh -server host:port</span><br></pre></td></tr></table></figure></p>
<p>host为zkServer的ip或者域名，port为配置的client端口</p>
<h2 id="结束会话"><a href="#结束会话" class="headerlink" title="结束会话"></a>结束会话</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">quit</span><br></pre></td></tr></table></figure>
<h2 id="创建节点"><a href="#创建节点" class="headerlink" title="创建节点"></a>创建节点</h2><h2 id="持久节点"><a href="#持久节点" class="headerlink" title="持久节点"></a>持久节点</h2><h3 id="有序节点"><a href="#有序节点" class="headerlink" title="有序节点"></a>有序节点</h3><p>创建时添加参数 -s 指定，所有有序节点会在节点，排序从0开始，每次创建有序节点会使序号加一<br>有序节点会在输入名称后加上序号作为节点名称创建节点<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create -s sortNode aaa</span><br></pre></td></tr></table></figure></p>
<h3 id="普通节点"><a href="#普通节点" class="headerlink" title="普通节点"></a>普通节点</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create node bbb</span><br></pre></td></tr></table></figure>
<p>没有指定为临时节点的节点默认都是持久节点</p>
<h2 id="临时节点。"><a href="#临时节点。" class="headerlink" title="临时节点。"></a>临时节点。</h2><p>临时节点在会话结束时就会被删除，通过创建时添加参数 -e 指定<br>临时节点不能存在子节点<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create -e tmpNode tmp</span><br></pre></td></tr></table></figure></p>
<p>临时节点也可以是有序节点<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create -s -e sortTmpNode sortTmp</span><br></pre></td></tr></table></figure></p>
<h2 id="ls命令"><a href="#ls命令" class="headerlink" title="ls命令"></a>ls命令</h2><p>ls命令与linux类似，展示给定路径的所有子节点<br>根路径为/<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ls /</span><br><span class="line">ls -w /</span><br></pre></td></tr></table></figure></p>
<p>-w参数代表开启该路径的一次watch，下一次且仅一次该路径下的子节点的增删事件会被监听到</p>
<h2 id="修改节点值"><a href="#修改节点值" class="headerlink" title="修改节点值"></a>修改节点值</h2><p>absPath—节点的绝对路径值，root为/<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">set</span> [absPath] value [version]</span><br></pre></td></tr></table></figure></p>
<p>version值可不传，用于并发控制，代表要求的为dataversion，即该节点被修改过几次，如果不符合，本次修改失败</p>
<h2 id="获取节点值"><a href="#获取节点值" class="headerlink" title="获取节点值"></a>获取节点值</h2><p>absPath—节点的绝对路径值，root为/<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">get -w [absPath]</span><br><span class="line">get -s [absPath]</span><br></pre></td></tr></table></figure></p>
<p>-w参数代表开启一次watch，下一次且仅一次对该节点的set事件将会被监听到<br>-s参数代表获取节点值并展示包括版本号、子节点个数等信息<br>-s输出格式如下<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">cZxid = 0x10000001d	//create node时分配的id</span><br><span class="line">ctime = Tue Jul 16 07:22:54 PDT 2019// 创建时间</span><br><span class="line">mZxid = 0x10000001f	//<span class="built_in">set</span>修改过以后变更的id</span><br><span class="line">mtime = Wed Jul 17 08:43:33 PDT 2019	//最后一次修改时间</span><br><span class="line">pZxid = 0x10000001d	//一开始为cZxid，子节点修改时变更</span><br><span class="line">cversion = 0	//子节点版本号，一开始为0，子节点修改时变更</span><br><span class="line">dataVersion = 2	//数据版本号，一开始为0，每修改一次+1</span><br><span class="line">aclVersion = 0	//acl权限版本号，一开始为0[create,<span class="built_in">read</span>,write,delete,admin]</span><br><span class="line">ephemeralOwner = 0x0	//临时节点才会有，持有者会话id</span><br><span class="line">dataLength = 1	//数据的字符串长度</span><br><span class="line">numChildren = 0	//子节点个数</span><br></pre></td></tr></table></figure></p>
<h2 id="删除节点"><a href="#删除节点" class="headerlink" title="删除节点"></a>删除节点</h2><p>absPath—节点的绝对路径值，root为/<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">delete [absPath]</span><br></pre></td></tr></table></figure></p>
<h3 id="更多精彩内容"><a href="#更多精彩内容" class="headerlink" title="更多精彩内容"></a>更多精彩内容</h3><h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-MBean与JMX" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/07/08/MBean与JMX/" class="article-date">
  	<time datetime="2019-07-08T15:17:12.569Z" itemprop="datePublished">2019-07-08</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/07/08/MBean与JMX/">
        MBean与JMX
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第二十二篇博客–MBean与JMX</p>
<h2 id="JMX"><a href="#JMX" class="headerlink" title="JMX"></a>JMX</h2><p>JMX(java Management Exetensions)在Java编程语言中定义了应用程序以及网络管理和监控的体系结构、设计模式、应用程序接口以及服务。<br>通常使用JMX来监控系统的运行状态或管理系统的某些方面，比如清空缓存、重新加载配置文件等<br>优点是可以非常容易的使应用程序被管理<br>伸缩性的架构使每个JMX Agent Service可以很容易的放入到Agent中，每个JMX的实现都提供几个核心的Agent Service，你也可以自己编写服务，服务可以很容易的部署，取消部署。<br>主要作用是提供接口，允许有不同的实现<br>简单来说，jmx是一个用来管理javaBean并可以进行监控的扩展规范，结合MBeanServer、rmi与http等可以作为一个服务监控和提供中心</p>
<h2 id="MBeanServer"><a href="#MBeanServer" class="headerlink" title="MBeanServer"></a>MBeanServer</h2><p>MBeanServer是JMX代理的核心组件。<br>它是在代理中向管理操作公开的对象的注册表。<br>向MBeanServer注册的任何对象都对管理应用程序可见。<br>MBeanServer仅公开MBean的管理接口，而不是它的直接对象引用。<br>您要从代理的Java VM外部管理的任何资源都必须在MBeanServer中注册为MBean。<br>MBeanServer还提供标准化接口，用于访问同一Java VM中的MBean，为本地对象提供操作可管理资源的所有好处。<br>需要注意的是，一般不使用MBeanServerFactory.createMBeanServer()，使用ManagementFactory.getPlatformMBeanServer()<br>后者在当前尚未在ManagementFactory中注册静态成员MBeanServer时，会先使用MBeanServerFactory.createMBeanServer()创建一个MBeanServer并将其注册到静态成员，后续每次调用会直接返回该成员实例<br>并且ManagementFactory.getPlatformMBeanServer()方法在第一次调用createMBeanServer()实例化MBeanServer以后，会读取PlatformComponent枚举的枚举值，将一些系统必要的MBean注册到MBeanServer<br>JConsole监控的MBeanServer就是在ManagementFactory中注册的静态成员MBeanServe<br>所以如果没有特殊配置的MBeanServer，jconsole是不会监控的</p>
<h2 id="Agent"><a href="#Agent" class="headerlink" title="Agent"></a>Agent</h2><p>Java Management Extensions（JMX）Agent是一个在Java虚拟机（Java VM）中运行的管理实体.<br>充当MBean和管理应用程序(JConsole等)之间的联络人<br>Agent只是一个规范，一般会封装我们创建和启动MBeanServer以及注册MBean的过程在一个Agent行为里，方便启动</p>
<h2 id="Agent-Service"><a href="#Agent-Service" class="headerlink" title="Agent Service"></a>Agent Service</h2><p>Agent Service是可以对MBeanServer中注册的MBean执行管理操作的对象。<br>通过将管理智能包含在代理中，JMX可帮助您构建更强大的管理解决方案。<br>Agent Service通常也是MBean，允许通过MBeanServer控制它们及其功能。<br>JMX规范定义了以下Agent Service：<br>通过管理applet（m-let）服务的动态类加载检索并实例化从网络动态下载的新类和本机库。<br>监视器观察MBean属性的数字或字符串值，并可以向其他对象通知几种类型的更改。<br>定时器提供调度机制，并且可以以预定间隔发送通知。<br>关系服务定义MBean之间的关联并维护关系的一致性。</p>
<h2 id="Protocol-Adaptors-and-Connectors"><a href="#Protocol-Adaptors-and-Connectors" class="headerlink" title="Protocol Adaptors and Connectors"></a>Protocol Adaptors and Connectors</h2><p>Protocol adaptors and connectors使代理可从远程(通过rmi或者http等协议)管理应用程序访问。<br>它们通过在MBean服务器中实例化和注册的MBean的特定协议提供视图。<br>它们使Java VM外部的管理应用程序能够：<br>获取或设置现有MBean的属性<br>对现有MBean执行操作<br>实例化并注册新的MBean<br>注册并接收MBean发出的通知<br>因此，要使JMX代理易于管理，它必须至少包含一个协议适配器或连接器。<br>Java SE平台包括标准RMI连接器。Agent可以包括任意数量的协议适配器和连接器，允许通过不同的协议同时远程管理和监视它</p>
<h3 id="Protocol-Adaptors"><a href="#Protocol-Adaptors" class="headerlink" title="Protocol Adaptors"></a>Protocol Adaptors</h3><p>Protocol Adaptors通过给定协议提供JMX代理的管理视图。<br>它们将MBean和MBean服务器的操作调整为给定协议中的表示，<br>并可能调整为不同的信息模型，例如SNMP。Java SE平台不包括任何协议适配器作为标准。<br>连接到Protocol Adaptors的管理应用程序通常特定于给定的协议。<br>这通常是依赖于特定管理协议的传统管理解决方案的情况。<br>它们不是通过MBean服务器的远程表示来访问JMX代理，而是通过映射到MBeanServer的操作来访问JMX代理。</p>
<h3 id="Connectors"><a href="#Connectors" class="headerlink" title="Connectors"></a>Connectors</h3><p>Connectors用于将代理与为JMX技术启用的远程管理应用程序连接，即使用JMX规范的分布式服务开发的管理应用程序。<br>这种通信涉及Agent中的连接器服务器和管理器中的连接器客户端。<br>这些组件以特定协议的方式透明地传递管理操作。<br>JMX Remote API为MBeanServer提供远程接口，管理应用程序可以通过该接口执行操作。<br>Connectors特定于给定协议，但管理应用程序可以无差别地使用任何Connectors，因为它们具有相同的远程接口。</p>
<h2 id="MBean"><a href="#MBean" class="headerlink" title="MBean"></a>MBean</h2><p>描述一个可管理的资源。是一个java对象，遵循以下一些规则：<br>1.必须是公用的，非抽象的类<br>2.必须有至少一个公用的构造器<br>3.必须实现它自己的相应的MBean接口或者实现javax.management.DynamicMBean接口<br>4.可选的，一个MBean可以实现javax.management.NotificationBroadcaster接口MBean的类型<br>MBean使用ObjectName以keyValue形式注册到MBeanServer上</p>
<h3 id="Standard-MBean"><a href="#Standard-MBean" class="headerlink" title="Standard MBean"></a>Standard MBean</h3><p>标准MBean，也是最简单的MBean，通过类实现的接口名称来识别MBean<br>接口名称以MBean结尾，实现类需要匹配接口名称的前半部分<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">public interface YytMBean&#123;</span><br><span class="line">	public void setName(String name);</span><br><span class="line">	public String getName();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">public class Yyt implements YytMBean&#123;</span><br><span class="line">	private String name;</span><br><span class="line">	@Override</span><br><span class="line">	public void setName(String name) &#123;</span><br><span class="line">		this.name = name;</span><br><span class="line">	&#125;</span><br><span class="line">	@Override</span><br><span class="line">	public String <span class="function"><span class="title">getName</span></span>() &#123;</span><br><span class="line">		<span class="built_in">return</span> name;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">public class YytAgent&#123;</span><br><span class="line">	private String domainName;</span><br><span class="line">	private int rmiPort;</span><br><span class="line">	public YytAgent(String domainName,int rmiPort) &#123;</span><br><span class="line">		this.domainName = domainName;</span><br><span class="line">		this.rmiPort = rmiPort;</span><br><span class="line">	&#125;</span><br><span class="line">	public void start() throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, IOException &#123;</span><br><span class="line">		//MBeanServerFactory.createMBeanServer();	//don<span class="string">'t use this</span></span><br><span class="line"><span class="string">		MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();	//use this instead</span></span><br><span class="line"><span class="string">		</span></span><br><span class="line"><span class="string">		YytMBean yytMBean = new Yyt();</span></span><br><span class="line"><span class="string">		ObjectName objectName = new ObjectName(domainName+":name=Yyt");</span></span><br><span class="line"><span class="string">		mBeanServer.registerMBean(yytMBean, objectName);</span></span><br><span class="line"><span class="string">		</span></span><br><span class="line"><span class="string">		LocateRegistry.createRegistry(rmiPort);//开启rmi端口监听，在jmxConnectorServer.start()时会根据serviceUrl建立一个rmiServer监听该rmi端口</span></span><br><span class="line"><span class="string">		</span></span><br><span class="line"><span class="string">		JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:"+rmiPort+"/"+domainName);</span></span><br><span class="line"><span class="string">        JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mBeanServer);</span></span><br><span class="line"><span class="string">        jmxConnectorServer.start();</span></span><br><span class="line"><span class="string">        //开启jmx协议的监控服务器，因为java自带rmi的工具和依赖，可以直接开启，通过jconsole等支持jmx协议的客户端可以监控MBeanServer	</span></span><br><span class="line"><span class="string">	&#125;</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure>
<h2 id="使用JConsole工具监控"><a href="#使用JConsole工具监控" class="headerlink" title="使用JConsole工具监控"></a>使用JConsole工具监控</h2><p>JConsole是java自带的监控程序，独立jre中没有，jdk下的jre具备</p>
<p>运行java应用时带上参数<br>-Dcom.sun.management.jmxremote<br>-Dcom.sun.management.jmxremote.port=8999<br>-Dcom.sun.management.jmxremote.authenticate=false<br>-Dcom.sun.management.jmxremote.ssl=false<br>关闭ssl和密码校验，开启远程监控端口8999，这时可以直接使用jconsole进行链接</p>
<p>或者在jdk\jre\lib\management目录下，复制一份jmxremote.password.template的文件，改名去掉.template后缀，并去掉文件末尾两行示例的用户名和密码注释，修改密码为自己想要的<br>运行加上参数<br>-Djava.rmi.server.hostname=应用所在服务器的ip或者域名<br>-Dcom.sun.management.jmxremote<br>-Dcom.sun.management.jmxremote.port=监控端口<br>-Dcom.sun.management.jmxremote.rmi.port=rmi监控端口（一般与监控端口保持一致）<br>-Dcom.sun.management.jmxremote.authenticate=true<br>-Dcom.sun.management.jmxremote.ssl=false<br>这时使用JConsole链接会提示需要输入用户名和密码</p>
<p>在命令行输入jconsole运行jconsole程序<br><img src="/blog/images/mbean/jconsole.jpg" alt="jconsole"><br>在jconsole的界面，因为是本地，直接选择本地进程，进入监控页面<br>点击导航栏的MBean即可看到我们注册的MBean在列表中<br><img src="/blog/images/mbean/mbean.jpg" alt="jconsole"><br>还可以对Bean的属性值进行查看和设置<br><img src="/blog/images/mbean/mbean属性.jpg" alt="jconsole"></p>
<h2 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h2><h3 id="ManagementFactory-getPlatformMBeanServer"><a href="#ManagementFactory-getPlatformMBeanServer" class="headerlink" title="ManagementFactory.getPlatformMBeanServer()"></a>ManagementFactory.getPlatformMBeanServer()</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">SecurityManager sm = System.getSecurityManager();</span><br><span class="line">      <span class="keyword">if</span> (sm != null) &#123;</span><br><span class="line">          Permission perm = new MBeanServerPermission(<span class="string">"createMBeanServer"</span>);</span><br><span class="line">          sm.checkPermission(perm);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (platformMBeanServer == null) &#123;</span><br><span class="line">          platformMBeanServer = MBeanServerFactory.createMBeanServer();</span><br><span class="line">          <span class="keyword">for</span> (PlatformComponent pc : PlatformComponent.values()) &#123;</span><br><span class="line">              List&lt;? extends PlatformManagedObject&gt; list =</span><br><span class="line">                  pc.getMXBeans(pc.getMXBeanInterface());</span><br><span class="line">              <span class="keyword">for</span> (PlatformManagedObject o : list) &#123;</span><br><span class="line">                  // Each PlatformComponent represents one management</span><br><span class="line">                  // interface. Some MXBean may extend another one.</span><br><span class="line">                  // The MXBean instances <span class="keyword">for</span> one platform component</span><br><span class="line">                  // (returned by pc.getMXBeans()) might be also</span><br><span class="line">                  // the MXBean instances <span class="keyword">for</span> another platform component.</span><br><span class="line">                  // e.g. com.sun.management.GarbageCollectorMXBean</span><br><span class="line">                  //</span><br><span class="line">                  // So need to check <span class="keyword">if</span> an MXBean instance is registered</span><br><span class="line">                  // before registering into the platform MBeanServer</span><br><span class="line">                  <span class="keyword">if</span> (!platformMBeanServer.isRegistered(o.getObjectName())) &#123;</span><br><span class="line">                      addMXBean(platformMBeanServer, o);</span><br><span class="line">                  &#125;</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;</span><br><span class="line">          HashMap&lt;ObjectName, DynamicMBean&gt; dynmbeans =</span><br><span class="line">                  ManagementFactoryHelper.getPlatformDynamicMBeans();</span><br><span class="line">          <span class="keyword">for</span> (Map.Entry&lt;ObjectName, DynamicMBean&gt; e : dynmbeans.entrySet()) &#123;</span><br><span class="line">              addDynamicMBean(platformMBeanServer, e.getValue(), e.getKey());</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">for</span> (final PlatformManagedObject o :</span><br><span class="line">                                     ExtendedPlatformComponent.getMXBeans()) &#123;</span><br><span class="line">              <span class="keyword">if</span> (!platformMBeanServer.isRegistered(o.getObjectName())) &#123;</span><br><span class="line">                  addMXBean(platformMBeanServer, o);</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="built_in">return</span> platformMBeanServer;</span><br></pre></td></tr></table></figure>
<p>ManagementFactory中定义一个platformMBeanServer成员来装载创建好的MBeanServer<br>在执行该方法的第一步会先校验当前安全权限<br>在platformMBeanServer尚未绑定一个实例时，会先使用MBeanServerFactory.createMBeanServer()实例一个MBeanServer对象<br>接下来主要是读取PlatformComponent.values()以及别的枚举类和helper类的值和成员，将系统预设的MBean注册到MBeanServer<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">enum PlatformComponent &#123;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Class loading system of the Java virtual machine.</span><br><span class="line">     */</span><br><span class="line">    CLASS_LOADING(</span><br><span class="line">        <span class="string">"java.lang.management.ClassLoadingMXBean"</span>,</span><br><span class="line">        <span class="string">"java.lang"</span>, <span class="string">"ClassLoading"</span>, defaultKeyProperties(),</span><br><span class="line">        <span class="literal">true</span>, // singleton</span><br><span class="line">        new MXBeanFetcher&lt;ClassLoadingMXBean&gt;() &#123;</span><br><span class="line">            public List&lt;ClassLoadingMXBean&gt; <span class="function"><span class="title">getMXBeans</span></span>() &#123;</span><br><span class="line">                <span class="built_in">return</span> Collections.singletonList(ManagementFactoryHelper.getClassLoadingMXBean());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;),</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Compilation system of the Java virtual machine.</span><br><span class="line">     */</span><br><span class="line">    COMPILATION(</span><br><span class="line">        <span class="string">"java.lang.management.CompilationMXBean"</span>,</span><br><span class="line">        <span class="string">"java.lang"</span>, <span class="string">"Compilation"</span>, defaultKeyProperties(),</span><br><span class="line">        <span class="literal">true</span>, // singleton</span><br><span class="line">        new MXBeanFetcher&lt;CompilationMXBean&gt;() &#123;</span><br><span class="line">            public List&lt;CompilationMXBean&gt; <span class="function"><span class="title">getMXBeans</span></span>() &#123;</span><br><span class="line">                CompilationMXBean m = ManagementFactoryHelper.getCompilationMXBean();</span><br><span class="line">                <span class="keyword">if</span> (m == null) &#123;</span><br><span class="line">                   <span class="built_in">return</span> Collections.emptyList();</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                   <span class="built_in">return</span> Collections.singletonList(m);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;),</span><br><span class="line">	.....</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="MBeanServerFactory-createMBeanServer"><a href="#MBeanServerFactory-createMBeanServer" class="headerlink" title="MBeanServerFactory.createMBeanServer()"></a>MBeanServerFactory.createMBeanServer()</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">   public static MBeanServer <span class="function"><span class="title">createMBeanServer</span></span>() &#123;</span><br><span class="line">       <span class="built_in">return</span> createMBeanServer(null);</span><br><span class="line">   &#125;</span><br><span class="line"> public static MBeanServer createMBeanServer(String domain)  &#123;</span><br><span class="line">	checkPermission(<span class="string">"createMBeanServer"</span>);</span><br><span class="line"></span><br><span class="line">	final MBeanServer mBeanServer = newMBeanServer(domain);</span><br><span class="line">	addMBeanServer(mBeanServer);</span><br><span class="line">	<span class="built_in">return</span> mBeanServer;</span><br><span class="line">&#125;</span><br><span class="line">public static MBeanServer newMBeanServer(String domain)  &#123;</span><br><span class="line">       checkPermission(<span class="string">"newMBeanServer"</span>);</span><br><span class="line"></span><br><span class="line">       // Get the builder. Creates a new one <span class="keyword">if</span> necessary.</span><br><span class="line">       //</span><br><span class="line">       final MBeanServerBuilder mbsBuilder = getNewMBeanServerBuilder();</span><br><span class="line">       // Returned value cannot be null.  NullPointerException <span class="keyword">if</span> violated.</span><br><span class="line"></span><br><span class="line">       synchronized(mbsBuilder) &#123;</span><br><span class="line">           final MBeanServerDelegate delegate  =</span><br><span class="line">                   mbsBuilder.newMBeanServerDelegate();</span><br><span class="line">           <span class="keyword">if</span> (delegate == null) &#123;</span><br><span class="line">               final String msg =</span><br><span class="line">                       <span class="string">"MBeanServerBuilder.newMBeanServerDelegate() "</span> +</span><br><span class="line">                       <span class="string">"returned null"</span>;</span><br><span class="line">               throw new JMRuntimeException(msg);</span><br><span class="line">           &#125;</span><br><span class="line">           final MBeanServer mbeanServer =</span><br><span class="line">                   mbsBuilder.newMBeanServer(domain,null,delegate);</span><br><span class="line">           <span class="keyword">if</span> (mbeanServer == null) &#123;</span><br><span class="line">               final String msg =</span><br><span class="line">                       <span class="string">"MBeanServerBuilder.newMBeanServer() returned null"</span>;</span><br><span class="line">               throw new JMRuntimeException(msg);</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="built_in">return</span> mbeanServer;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<p>createMBeanServer方法会调用newMBeanServer方法实例化MBeanServer<br>newMBeanServer方法中会先使用MBeanServerBuilder.newMBeanServerDelegate()实例化一个MBeanServerBuilder<br>再使用MBeanServerBuilder.newMBeanServer(domain,null,delegate)方法实例化一个MBeanServer<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">public MBeanServer newMBeanServer(String  defaultDomain,</span><br><span class="line">                                    MBeanServer  outer,</span><br><span class="line">                                    MBeanServerDelegate delegate) &#123;</span><br><span class="line">      // By default, MBeanServerInterceptors are disabled.</span><br><span class="line">      // Use com.sun.jmx.mbeanserver.MBeanServerBuilder to obtain</span><br><span class="line">      // MBeanServers on <span class="built_in">which</span> MBeanServerInterceptors are enabled.</span><br><span class="line">      <span class="built_in">return</span> JmxMBeanServer.newMBeanServer(defaultDomain,outer,delegate,</span><br><span class="line">                                           <span class="literal">false</span>);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></p>
<p>newMBeanServer方法实际实例化并返回的是一个JmxMBeanServer对象<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line">//<span class="keyword">in</span> JmxMBeanServer</span><br><span class="line">   public static MBeanServer newMBeanServer(String defaultDomain,</span><br><span class="line">                                            MBeanServer outer,</span><br><span class="line">                                            MBeanServerDelegate delegate,</span><br><span class="line">                                            boolean interceptors) &#123;</span><br><span class="line">       // Determine whether to use fair locking <span class="keyword">for</span> the repository.</span><br><span class="line">       // Default is <span class="literal">true</span>.</span><br><span class="line">       final boolean fairLock = DEFAULT_FAIR_LOCK_POLICY;</span><br><span class="line"></span><br><span class="line">       checkNewMBeanServerPermission();</span><br><span class="line"></span><br><span class="line">       // This constructor happens to disregard the value of the interceptors</span><br><span class="line">       // flag - that is, it always uses the default value - <span class="literal">false</span>.</span><br><span class="line">       // This is admitedly a bug, but we chose not to fix it <span class="keyword">for</span> now</span><br><span class="line">       // since we would rather not have anybody depending on the Sun private</span><br><span class="line">       // interceptor APIs - <span class="built_in">which</span> is most probably going to be removed and</span><br><span class="line">       // replaced by a public (javax) feature <span class="keyword">in</span> the future.</span><br><span class="line">       //</span><br><span class="line">       <span class="built_in">return</span> new JmxMBeanServer(defaultDomain,outer,delegate,null,</span><br><span class="line">                                 interceptors,fairLock);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">JmxMBeanServer(String domain, MBeanServer outer,</span><br><span class="line">                  MBeanServerDelegate    delegate,</span><br><span class="line">                  MBeanInstantiator      instantiator,</span><br><span class="line">                  boolean                interceptors,</span><br><span class="line">                  boolean                fairLock)  &#123;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (instantiator == null) &#123;</span><br><span class="line">           final ModifiableClassLoaderRepository</span><br><span class="line">               clr = new ClassLoaderRepositorySupport();</span><br><span class="line">           instantiator = new MBeanInstantiator(clr);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       final MBeanInstantiator fInstantiator = instantiator;</span><br><span class="line">       this.secureClr = new</span><br><span class="line">           SecureClassLoaderRepository(AccessController.doPrivileged(new PrivilegedAction&lt;ClassLoaderRepository&gt;() &#123;</span><br><span class="line">               @Override</span><br><span class="line">               public ClassLoaderRepository <span class="function"><span class="title">run</span></span>() &#123;</span><br><span class="line">                   <span class="built_in">return</span> fInstantiator.getClassLoaderRepository();</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;)</span><br><span class="line">       );</span><br><span class="line">       <span class="keyword">if</span> (delegate == null)</span><br><span class="line">           delegate = new MBeanServerDelegateImpl();</span><br><span class="line">       <span class="keyword">if</span> (outer == null)</span><br><span class="line">           outer = this;</span><br><span class="line"></span><br><span class="line">       this.instantiator = instantiator;</span><br><span class="line">       this.mBeanServerDelegateObject = delegate;</span><br><span class="line">       this.outerShell   = outer;</span><br><span class="line"></span><br><span class="line">       final Repository repository = new Repository(domain);</span><br><span class="line">       this.mbsInterceptor =</span><br><span class="line">           new DefaultMBeanServerInterceptor(outer, delegate, instantiator,</span><br><span class="line">                                             repository);</span><br><span class="line">       this.interceptorsEnabled = interceptors;</span><br><span class="line">       initialize();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>实例化一个Repository并聚合到新生成的一个DefaultMBeanServerInterceptor<br>Repository就是MBeanServer存放MBean的容器类，其内部维护一个Map&lt;String,Map&lt;String,NamedObject&gt;&gt; domainTb的成员<br>此处由于是第一次实例化MBeanServer，传入的domain为null，最终会使用ServiceName的静态的成员值DefaultDomain作为默认域<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">public class ServiceName &#123;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * The default domain.</span><br><span class="line">     * &lt;BR&gt;</span><br><span class="line">     * The value is &lt;CODE&gt;DefaultDomain&lt;/CODE&gt;.</span><br><span class="line">     */</span><br><span class="line">    public static final String DOMAIN = <span class="string">"DefaultDomain"</span>;</span><br><span class="line">	.....</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>NamedObject类是聚合ObjectName和DynamicMBean的辅助类<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">public class NamedObject  &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Object name.</span><br><span class="line">     */</span><br><span class="line">    private final ObjectName name;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Object reference.</span><br><span class="line">     */</span><br><span class="line">    private final DynamicMBean object;</span><br><span class="line">	.....</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>后续的注册bean和获取bean等方法实现最终都是由mbsInterceptor成员去操作</p>
<h3 id="LocateRegistry-createRegistry"><a href="#LocateRegistry-createRegistry" class="headerlink" title="LocateRegistry.createRegistry"></a>LocateRegistry.createRegistry</h3><p>创建注册中心并开启端口监听，返回创建的RegistryImpl实例<br>源码分析请参照本人的另一篇博客——<a href="https://blog.csdn.net/weixin_44627989/article/details/93055791" target="_blank" rel="noopener">java-rmi源码解析</a></p>
<h3 id="MBeanServer-registerMBean"><a href="#MBeanServer-registerMBean" class="headerlink" title="MBeanServer.registerMBean"></a>MBeanServer.registerMBean</h3><p>在JmxMBeanServer中的实现<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">public ObjectInstance registerMBean(Object object, ObjectName name)</span><br><span class="line">       throws InstanceAlreadyExistsException, MBeanRegistrationException,</span><br><span class="line">              NotCompliantMBeanException  &#123;</span><br><span class="line"></span><br><span class="line">       <span class="built_in">return</span> mbsInterceptor.registerMBean(object, cloneObjectName(name));</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>上面讲到，默认的MBeanServer的mbsInterceptor成员由new DefaultMBeanServerInterceptor(outer, delegate, instantiator,repository)实例化<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">public ObjectInstance registerMBean(Object object, ObjectName name)</span><br><span class="line">       throws InstanceAlreadyExistsException, MBeanRegistrationException,</span><br><span class="line">       NotCompliantMBeanException  &#123;</span><br><span class="line"></span><br><span class="line">       // ------------------------------</span><br><span class="line">       // ------------------------------</span><br><span class="line">       Class&lt;?&gt; theClass = object.getClass();</span><br><span class="line"></span><br><span class="line">       Introspector.checkCompliance(theClass);</span><br><span class="line"></span><br><span class="line">       final String infoClassName = getNewMBeanClassName(object);</span><br><span class="line"></span><br><span class="line">       checkMBeanPermission(infoClassName, null, name, <span class="string">"registerMBean"</span>);</span><br><span class="line">       checkMBeanTrustPermission(theClass);</span><br><span class="line"></span><br><span class="line">       <span class="built_in">return</span> registerObject(infoClassName, object, name);</span><br><span class="line">   &#125;</span><br><span class="line">private ObjectInstance registerObject(String classname,</span><br><span class="line">                                         Object object, ObjectName name)</span><br><span class="line">       throws InstanceAlreadyExistsException,</span><br><span class="line">              MBeanRegistrationException,</span><br><span class="line">              NotCompliantMBeanException &#123;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (object == null) &#123;</span><br><span class="line">           final RuntimeException wrapped =</span><br><span class="line">               new IllegalArgumentException(<span class="string">"Cannot add null object"</span>);</span><br><span class="line">           throw new RuntimeOperationsException(wrapped,</span><br><span class="line">                       <span class="string">"Exception occurred trying to register the MBean"</span>);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       DynamicMBean mbean = Introspector.makeDynamicMBean(object);</span><br><span class="line"></span><br><span class="line">       <span class="built_in">return</span> registerDynamicMBean(classname, mbean, name);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>DefaultMBeanServerInterceptor会将要注册的MBean封装为DynamicMBean，调用registerDynamicMBean进行注册<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line">private ObjectInstance registerDynamicMBean(String classname,</span><br><span class="line">                                               DynamicMBean mbean,</span><br><span class="line">                                               ObjectName name)</span><br><span class="line">       throws InstanceAlreadyExistsException,</span><br><span class="line">              MBeanRegistrationException,</span><br><span class="line">              NotCompliantMBeanException &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">       name = nonDefaultDomain(name);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) &#123;</span><br><span class="line">           MBEANSERVER_LOGGER.logp(Level.FINER,</span><br><span class="line">                   DefaultMBeanServerInterceptor.class.getName(),</span><br><span class="line">                   <span class="string">"registerMBean"</span>, <span class="string">"ObjectName = "</span> + name);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       ObjectName logicalName = preRegister(mbean, server, name);</span><br><span class="line"></span><br><span class="line">       // preRegister returned successfully, so from this point on we</span><br><span class="line">       // must call postRegister(<span class="literal">false</span>) <span class="keyword">if</span> there is any problem.</span><br><span class="line">       boolean registered = <span class="literal">false</span>;</span><br><span class="line">       boolean registerFailed = <span class="literal">false</span>;</span><br><span class="line">       ResourceContext context = null;</span><br><span class="line"></span><br><span class="line">       try &#123;</span><br><span class="line">           <span class="keyword">if</span> (mbean instanceof DynamicMBean2) &#123;</span><br><span class="line">               try &#123;</span><br><span class="line">                   ((DynamicMBean2) mbean).preRegister2(server, logicalName);</span><br><span class="line">                   registerFailed = <span class="literal">true</span>;  // until we succeed</span><br><span class="line">               &#125; catch (Exception e) &#123;</span><br><span class="line">                   <span class="keyword">if</span> (e instanceof RuntimeException)</span><br><span class="line">                       throw (RuntimeException) e;</span><br><span class="line">                   <span class="keyword">if</span> (e instanceof InstanceAlreadyExistsException)</span><br><span class="line">                       throw (InstanceAlreadyExistsException) e;</span><br><span class="line">                   throw new RuntimeException(e);</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           <span class="keyword">if</span> (logicalName != name &amp;&amp; logicalName != null) &#123;</span><br><span class="line">               logicalName =</span><br><span class="line">                       ObjectName.getInstance(nonDefaultDomain(logicalName));</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           checkMBeanPermission(classname, null, logicalName, <span class="string">"registerMBean"</span>);</span><br><span class="line"></span><br><span class="line">           <span class="keyword">if</span> (logicalName == null) &#123;</span><br><span class="line">               final RuntimeException wrapped =</span><br><span class="line">                   new IllegalArgumentException(<span class="string">"No object name specified"</span>);</span><br><span class="line">               throw new RuntimeOperationsException(wrapped,</span><br><span class="line">                           <span class="string">"Exception occurred trying to register the MBean"</span>);</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           final Object resource = getResource(mbean);</span><br><span class="line"></span><br><span class="line">           // Register the MBean with the repository.</span><br><span class="line">           // Returns the resource context that was used.</span><br><span class="line">           // The returned context does nothing <span class="keyword">for</span> regular MBeans.</span><br><span class="line">           // For ClassLoader MBeans the context makes it possible to register these</span><br><span class="line">           // objects with the appropriate framework artifacts, such as</span><br><span class="line">           // the CLR, from within the repository lock.</span><br><span class="line">           // In <span class="keyword">case</span> of success, we also need to call context.done() at the</span><br><span class="line">           // end of this method.</span><br><span class="line">           //</span><br><span class="line">           context = registerWithRepository(resource, mbean, logicalName);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">           registerFailed = <span class="literal">false</span>;</span><br><span class="line">           registered = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">       &#125; finally &#123;</span><br><span class="line">           try &#123;</span><br><span class="line">               postRegister(logicalName, mbean, registered, registerFailed);</span><br><span class="line">           &#125; finally &#123;</span><br><span class="line">               <span class="keyword">if</span> (registered &amp;&amp; context!=null) context.done();</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="built_in">return</span> new ObjectInstance(logicalName, classname);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>registerDynamicMBean方法最终调用registerWithRepository方法将DynamicMBean对象注册到Repository里<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">private ResourceContext registerWithRepository(</span><br><span class="line">           final Object resource,</span><br><span class="line">           final DynamicMBean object,</span><br><span class="line">           final ObjectName logicalName)</span><br><span class="line">           throws InstanceAlreadyExistsException,</span><br><span class="line">           MBeanRegistrationException &#123;</span><br><span class="line"></span><br><span class="line">       // Creates a registration context, <span class="keyword">if</span> needed.</span><br><span class="line">       //</span><br><span class="line">       final ResourceContext context =</span><br><span class="line">               makeResourceContextFor(resource, logicalName);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">       repository.addMBean(object, logicalName, context);</span><br><span class="line">       // May throw InstanceAlreadyExistsException</span><br><span class="line"></span><br><span class="line">       // ---------------------</span><br><span class="line">       // Send create event</span><br><span class="line">       // ---------------------</span><br><span class="line">       <span class="keyword">if</span> (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) &#123;</span><br><span class="line">           MBEANSERVER_LOGGER.logp(Level.FINER,</span><br><span class="line">                   DefaultMBeanServerInterceptor.class.getName(),</span><br><span class="line">                   <span class="string">"addObject"</span>, <span class="string">"Send create notification of object "</span> +</span><br><span class="line">                   logicalName.getCanonicalName());</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       sendNotification(</span><br><span class="line">               MBeanServerNotification.REGISTRATION_NOTIFICATION,</span><br><span class="line">               logicalName);</span><br><span class="line"></span><br><span class="line">       <span class="built_in">return</span> context;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>前面已经讲过在创建DefaultMBeanServerInterceptor实例时，会实例化一个repository对象并装载到成员<br>注册bean会调用repository的addMBean方法<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line">public void addMBean(final DynamicMBean object, ObjectName name,</span><br><span class="line">           final RegistrationContext context)</span><br><span class="line">       throws InstanceAlreadyExistsException &#123;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) &#123;</span><br><span class="line">           MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(),</span><br><span class="line">                   <span class="string">"addMBean"</span>, <span class="string">"name = "</span> + name);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       // Extract the domain name.</span><br><span class="line">       String dom = name.getDomain().intern();</span><br><span class="line">       boolean to_default_domain = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">       // Set domain to default <span class="keyword">if</span> domain is empty and not already <span class="built_in">set</span></span><br><span class="line">       <span class="keyword">if</span> (dom.length() == 0)</span><br><span class="line">           name = Util.newObjectName(domain + name.toString());</span><br><span class="line"></span><br><span class="line">       // Do we have default domain ?</span><br><span class="line">       <span class="keyword">if</span> (dom == domain) &#123;  // ES: OK (dom &amp; domain are interned)</span><br><span class="line">           to_default_domain = <span class="literal">true</span>;</span><br><span class="line">           dom = domain;</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           to_default_domain = <span class="literal">false</span>;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       // Validate name <span class="keyword">for</span> an object</span><br><span class="line">       <span class="keyword">if</span> (name.isPattern()) &#123;</span><br><span class="line">           throw new RuntimeOperationsException(</span><br><span class="line">            new IllegalArgumentException(<span class="string">"Repository: cannot add mbean for "</span> +</span><br><span class="line">                                         <span class="string">"pattern name "</span> + name.toString()));</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       lock.writeLock().lock();</span><br><span class="line">       try &#123;</span><br><span class="line">           // Domain cannot be JMImplementation <span class="keyword">if</span> entry does not exist</span><br><span class="line">           <span class="keyword">if</span> ( !to_default_domain &amp;&amp;</span><br><span class="line">                   dom.equals(<span class="string">"JMImplementation"</span>) &amp;&amp;</span><br><span class="line">                   domainTb.containsKey(<span class="string">"JMImplementation"</span>)) &#123;</span><br><span class="line">               throw new RuntimeOperationsException(</span><br><span class="line">                       new IllegalArgumentException(</span><br><span class="line">                       <span class="string">"Repository: domain name cannot be JMImplementation"</span>));</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           // If domain does not already exist, add it to the <span class="built_in">hash</span> table</span><br><span class="line">           final Map&lt;String,NamedObject&gt; moiTb = domainTb.get(dom);</span><br><span class="line">           <span class="keyword">if</span> (moiTb == null) &#123;</span><br><span class="line">               addNewDomMoi(object, dom, name, context);</span><br><span class="line">               <span class="built_in">return</span>;</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">               // Add instance <span class="keyword">if</span> not already present</span><br><span class="line">               String cstr = name.getCanonicalKeyPropertyListString();</span><br><span class="line">               NamedObject elmt= moiTb.get(cstr);</span><br><span class="line">               <span class="keyword">if</span> (elmt != null) &#123;</span><br><span class="line">                   throw new InstanceAlreadyExistsException(name.toString());</span><br><span class="line">               &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                   nbElements++;</span><br><span class="line">                   addMoiToTb(object,name,cstr,moiTb,context);</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">       &#125; finally &#123;</span><br><span class="line">           lock.writeLock().unlock();</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line">private void addMoiToTb(final DynamicMBean object,</span><br><span class="line">           final ObjectName name,</span><br><span class="line">           final String key,</span><br><span class="line">           final Map&lt;String,NamedObject&gt; moiTb,</span><br><span class="line">           final RegistrationContext context) &#123;</span><br><span class="line">       registering(context);</span><br><span class="line">       moiTb.put(key,new NamedObject(name, object));</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>domainTb成员就是前面说过的repository中存储MBean的Map，类型是Map&lt;String,Map&lt;String,NamedObject&gt;&gt;<br>add方法里会通过writelock进行同步<br>如果获取到的moiTb，代表该MBean的域名尚未加入domainTb，会将域名加入map并将MBean也加入</p>
<h2 id="JmxMBeanServer"><a href="#JmxMBeanServer" class="headerlink" title="JmxMBeanServer"></a>JmxMBeanServer</h2><p>JmxMBeanServer<br>不提供直接返回MBean对象的方法<br>但提供了很多行为如 setAttributes，getAttribute，invoke等方法，根据ObjectName参数进行MBean识别<br>调用成员的DefaultMBeanServerInterceptor实例去执行对应的修改属性，获取属性值，执行方法等行为<br>DefaultMBeanServerInterceptor最终是获取repository中的MBean去执行对应行为<br>通过getObjectInstance可以获得该MBean的类名和域名信息的封装对象<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">public ObjectInstance getObjectInstance(ObjectName name)</span><br><span class="line">       throws InstanceNotFoundException &#123;</span><br><span class="line"></span><br><span class="line">       <span class="built_in">return</span> mbsInterceptor.getObjectInstance(cloneObjectName(name));</span><br><span class="line">   &#125;</span><br><span class="line">public Object getAttribute(ObjectName name, String attribute)</span><br><span class="line">       throws MBeanException, AttributeNotFoundException,</span><br><span class="line">              InstanceNotFoundException, ReflectionException &#123;</span><br><span class="line"></span><br><span class="line">       <span class="built_in">return</span> mbsInterceptor.getAttribute(cloneObjectName(name), attribute);</span><br><span class="line">   &#125;</span><br><span class="line">public void setAttribute(ObjectName name, Attribute attribute)</span><br><span class="line">       throws InstanceNotFoundException, AttributeNotFoundException,</span><br><span class="line">              InvalidAttributeValueException, MBeanException,</span><br><span class="line">              ReflectionException  &#123;</span><br><span class="line"></span><br><span class="line">       mbsInterceptor.setAttribute(cloneObjectName(name),</span><br><span class="line">                                   cloneAttribute(attribute));</span><br><span class="line">   &#125;</span><br><span class="line">public Object invoke(ObjectName name, String operationName,</span><br><span class="line">                        Object params[], String signature[])</span><br><span class="line">       throws InstanceNotFoundException, MBeanException,</span><br><span class="line">              ReflectionException &#123;</span><br><span class="line">       <span class="built_in">return</span> mbsInterceptor.invoke(cloneObjectName(name), operationName,</span><br><span class="line">                                    params, signature);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="JMXConnectorServerFactory-newJMXConnectorServer"><a href="#JMXConnectorServerFactory-newJMXConnectorServer" class="headerlink" title="JMXConnectorServerFactory.newJMXConnectorServer"></a>JMXConnectorServerFactory.newJMXConnectorServer</h2><p>该方法创建一个JMXConnectorServer，将MBeanServer和JMXServiceURL传递过去<br>最终url、MbeanServer等对象会被聚合到JMXConnectorServer，作为成员<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line">public static JMXConnectorServer</span><br><span class="line">    newJMXConnectorServer(JMXServiceURL serviceURL,</span><br><span class="line">                          Map&lt;String,?&gt; environment,</span><br><span class="line">                          MBeanServer mbeanServer)</span><br><span class="line">        throws IOException &#123;</span><br><span class="line">    Map&lt;String, Object&gt; envcopy;</span><br><span class="line">    <span class="keyword">if</span> (environment == null)</span><br><span class="line">        envcopy = new HashMap&lt;String, Object&gt;();</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        EnvHelp.checkAttributes(environment);</span><br><span class="line">        envcopy = new HashMap&lt;String, Object&gt;(environment);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    final Class&lt;JMXConnectorServerProvider&gt; targetInterface =</span><br><span class="line">            JMXConnectorServerProvider.class;</span><br><span class="line">    final ClassLoader loader =</span><br><span class="line">        JMXConnectorFactory.resolveClassLoader(envcopy);</span><br><span class="line">    final String protocol = serviceURL.getProtocol();</span><br><span class="line">    final String providerClassName = <span class="string">"ServerProvider"</span>;</span><br><span class="line"></span><br><span class="line">    JMXConnectorServerProvider provider =</span><br><span class="line">        JMXConnectorFactory.getProvider(serviceURL,</span><br><span class="line">                                        envcopy,</span><br><span class="line">                                        providerClassName,</span><br><span class="line">                                        targetInterface,</span><br><span class="line">                                        loader);</span><br><span class="line"></span><br><span class="line">    IOException exception = null;</span><br><span class="line">    <span class="keyword">if</span> (provider == null) &#123;</span><br><span class="line">        // Loader is null when context class loader is <span class="built_in">set</span> to null</span><br><span class="line">        // and no loader has been provided <span class="keyword">in</span> map.</span><br><span class="line">        // com.sun.jmx.remote.util.Service class extracted from j2se</span><br><span class="line">        // provider search algorithm doesn<span class="string">'t handle well null classloader.</span></span><br><span class="line"><span class="string">        if (loader != null) &#123;</span></span><br><span class="line"><span class="string">            try &#123;</span></span><br><span class="line"><span class="string">                JMXConnectorServer connection =</span></span><br><span class="line"><span class="string">                    getConnectorServerAsService(loader,</span></span><br><span class="line"><span class="string">                                                serviceURL,</span></span><br><span class="line"><span class="string">                                                envcopy,</span></span><br><span class="line"><span class="string">                                                mbeanServer);</span></span><br><span class="line"><span class="string">                if (connection != null)</span></span><br><span class="line"><span class="string">                    return connection;</span></span><br><span class="line"><span class="string">            &#125; catch (JMXProviderException e) &#123;</span></span><br><span class="line"><span class="string">                throw e;</span></span><br><span class="line"><span class="string">            &#125; catch (IOException e) &#123;</span></span><br><span class="line"><span class="string">                exception = e;</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">        provider =</span></span><br><span class="line"><span class="string">            JMXConnectorFactory.getProvider(</span></span><br><span class="line"><span class="string">                protocol,</span></span><br><span class="line"><span class="string">                PROTOCOL_PROVIDER_DEFAULT_PACKAGE,</span></span><br><span class="line"><span class="string">                JMXConnectorFactory.class.getClassLoader(),</span></span><br><span class="line"><span class="string">                providerClassName,</span></span><br><span class="line"><span class="string">                targetInterface);</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    if (provider == null) &#123;</span></span><br><span class="line"><span class="string">        MalformedURLException e =</span></span><br><span class="line"><span class="string">            new MalformedURLException("Unsupported protocol: " + protocol);</span></span><br><span class="line"><span class="string">        if (exception == null) &#123;</span></span><br><span class="line"><span class="string">            throw e;</span></span><br><span class="line"><span class="string">        &#125; else &#123;</span></span><br><span class="line"><span class="string">            throw EnvHelp.initCause(e, exception);</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    envcopy = Collections.unmodifiableMap(envcopy);</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    return provider.newJMXConnectorServer(serviceURL,</span></span><br><span class="line"><span class="string">                                          envcopy,</span></span><br><span class="line"><span class="string">                                          mbeanServer);</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure></p>
<p>JMXConnectorServerFactory的newJMXConnectorServer最终是返回由provider生产的JMXConnectorServer<br>provider是接口，在jdk里对于该方法有两个实现类，一个是返回基于rmi协议，另一个是返回基于iiop协议的<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">//ServiceProvider <span class="keyword">for</span> rmi</span><br><span class="line">public JMXConnectorServer newJMXConnectorServer(JMXServiceURL serviceURL,</span><br><span class="line">                                                   Map&lt;String,?&gt; environment,</span><br><span class="line">                                                   MBeanServer mbeanServer)</span><br><span class="line">           throws IOException &#123;</span><br><span class="line">       <span class="keyword">if</span> (!serviceURL.getProtocol().equals(<span class="string">"rmi"</span>)) &#123;</span><br><span class="line">           throw new MalformedURLException(<span class="string">"Protocol not rmi: "</span> +</span><br><span class="line">                                           serviceURL.getProtocol());</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="built_in">return</span> new RMIConnectorServer(serviceURL, environment, mbeanServer);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>需要留意的是，在serviceProvider中调用生成RMIConnectorServer时，最终会调用传递(RMIServerImpl) null为形参参数的构造函数构造<br>也就是生成的RMIConnectorServer中是没有指定服务端对象的<br>RMIServerImpl会在JMXConnectorServer调用start方法后结合前面聚合到JMXConnectorServer中的rmi的url去实例化<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">   public RMIConnectorServer(JMXServiceURL url, Map&lt;String,?&gt; environment,</span><br><span class="line">                             MBeanServer mbeanServer)</span><br><span class="line">           throws IOException &#123;</span><br><span class="line">       this(url, environment, (RMIServerImpl) null, mbeanServer);</span><br><span class="line">   &#125;</span><br><span class="line">public RMIConnectorServer(JMXServiceURL url, Map&lt;String,?&gt; environment,</span><br><span class="line">                             RMIServerImpl rmiServerImpl,</span><br><span class="line">                             MBeanServer mbeanServer)</span><br><span class="line">           throws IOException &#123;</span><br><span class="line">       super(mbeanServer);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (url == null) throw new</span><br><span class="line">           IllegalArgumentException(<span class="string">"Null JMXServiceURL"</span>);</span><br><span class="line">       <span class="keyword">if</span> (rmiServerImpl == null) &#123;</span><br><span class="line">           final String prt = url.getProtocol();</span><br><span class="line">           <span class="keyword">if</span> (prt == null || !(prt.equals(<span class="string">"rmi"</span>) || prt.equals(<span class="string">"iiop"</span>))) &#123;</span><br><span class="line">               final String msg = <span class="string">"Invalid protocol type: "</span> + prt;</span><br><span class="line">               throw new MalformedURLException(msg);</span><br><span class="line">           &#125;</span><br><span class="line">           final String urlPath = url.getURLPath();</span><br><span class="line">           <span class="keyword">if</span> (!urlPath.equals(<span class="string">""</span>)</span><br><span class="line">               &amp;&amp; !urlPath.equals(<span class="string">"/"</span>)</span><br><span class="line">               &amp;&amp; !urlPath.startsWith(<span class="string">"/jndi/"</span>)) &#123;</span><br><span class="line">               final String msg = <span class="string">"URL path must be empty or start with "</span> +</span><br><span class="line">                   <span class="string">"/jndi/"</span>;</span><br><span class="line">               throw new MalformedURLException(msg);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (environment == null)</span><br><span class="line">           this.attributes = Collections.emptyMap();</span><br><span class="line">       <span class="keyword">else</span> &#123;</span><br><span class="line">           EnvHelp.checkAttributes(environment);</span><br><span class="line">           this.attributes = Collections.unmodifiableMap(environment);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       this.address = url;</span><br><span class="line">       this.rmiServerImpl = rmiServerImpl;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="jmxConnectorServer-start"><a href="#jmxConnectorServer-start" class="headerlink" title="jmxConnectorServer.start()"></a>jmxConnectorServer.start()</h2><p>启动JMX链接服务器，该过程会完成rmiServerImpl的实例化（开启监听rmi服务端口）<br>所以如果前面没有调用LocateRegistry.createRegistry(rmiPort)对端口进行注册，会导致报错<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br></pre></td><td class="code"><pre><span class="line">public synchronized void start() throws IOException &#123;</span><br><span class="line">        final boolean tracing = logger.traceOn();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (state == STARTED) &#123;</span><br><span class="line">            <span class="keyword">if</span> (tracing) logger.trace(<span class="string">"start"</span>, <span class="string">"already started"</span>);</span><br><span class="line">            <span class="built_in">return</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (state == STOPPED) &#123;</span><br><span class="line">            <span class="keyword">if</span> (tracing) logger.trace(<span class="string">"start"</span>, <span class="string">"already stopped"</span>);</span><br><span class="line">            throw new IOException(<span class="string">"The server has been stopped."</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (getMBeanServer() == null)</span><br><span class="line">            throw new IllegalStateException(<span class="string">"This connector server is not "</span> +</span><br><span class="line">                                            <span class="string">"attached to an MBean server"</span>);</span><br><span class="line"></span><br><span class="line">        // Check the internal access file property to see</span><br><span class="line">        // <span class="keyword">if</span> an MBeanServerForwarder is to be provided</span><br><span class="line">        //</span><br><span class="line">        <span class="keyword">if</span> (attributes != null) &#123;</span><br><span class="line">            // Check <span class="keyword">if</span> access file property is specified</span><br><span class="line">            //</span><br><span class="line">            String accessFile =</span><br><span class="line">                (String) attributes.get(<span class="string">"jmx.remote.x.access.file"</span>);</span><br><span class="line">            <span class="keyword">if</span> (accessFile != null) &#123;</span><br><span class="line">                // Access file property specified, create an instance</span><br><span class="line">                // of the MBeanServerFileAccessController class</span><br><span class="line">                //</span><br><span class="line">                MBeanServerForwarder mbsf;</span><br><span class="line">                try &#123;</span><br><span class="line">                    mbsf = new MBeanServerFileAccessController(accessFile);</span><br><span class="line">                &#125; catch (IOException e) &#123;</span><br><span class="line">                    throw EnvHelp.initCause(</span><br><span class="line">                        new IllegalArgumentException(e.getMessage()), e);</span><br><span class="line">                &#125;</span><br><span class="line">                // Set the MBeanServerForwarder</span><br><span class="line">                //</span><br><span class="line">                setMBeanServerForwarder(mbsf);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        try &#123;</span><br><span class="line">            <span class="keyword">if</span> (tracing) logger.trace(<span class="string">"start"</span>, <span class="string">"setting default class loader"</span>);</span><br><span class="line">            defaultClassLoader = EnvHelp.resolveServerClassLoader(</span><br><span class="line">                    attributes, getMBeanServer());</span><br><span class="line">        &#125; catch (InstanceNotFoundException infc) &#123;</span><br><span class="line">            IllegalArgumentException x = new</span><br><span class="line">                IllegalArgumentException(<span class="string">"ClassLoader not found: "</span>+infc);</span><br><span class="line">            throw EnvHelp.initCause(x,infc);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (tracing) logger.trace(<span class="string">"start"</span>, <span class="string">"setting RMIServer object"</span>);</span><br><span class="line">        final RMIServerImpl rmiServer;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (rmiServerImpl != null)</span><br><span class="line">            rmiServer = rmiServerImpl;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            rmiServer = newServer();</span><br><span class="line"></span><br><span class="line">        rmiServer.setMBeanServer(getMBeanServer());</span><br><span class="line">        rmiServer.setDefaultClassLoader(defaultClassLoader);</span><br><span class="line">        rmiServer.setRMIConnectorServer(this);</span><br><span class="line">        rmiServer.export();</span><br><span class="line"></span><br><span class="line">        try &#123;</span><br><span class="line">            <span class="keyword">if</span> (tracing) logger.trace(<span class="string">"start"</span>, <span class="string">"getting RMIServer object to export"</span>);</span><br><span class="line">            final RMIServer objref = objectToBind(rmiServer, attributes);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (address != null &amp;&amp; address.getURLPath().startsWith(<span class="string">"/jndi/"</span>)) &#123;</span><br><span class="line">                final String jndiUrl = address.getURLPath().substring(6);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (tracing)</span><br><span class="line">                    logger.trace(<span class="string">"start"</span>, <span class="string">"Using external directory: "</span> + jndiUrl);</span><br><span class="line"></span><br><span class="line">                String stringBoolean = (String) attributes.get(JNDI_REBIND_ATTRIBUTE);</span><br><span class="line">                final boolean rebind = EnvHelp.computeBooleanFromString( stringBoolean );</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (tracing)</span><br><span class="line">                    logger.trace(<span class="string">"start"</span>, JNDI_REBIND_ATTRIBUTE + <span class="string">"="</span> + rebind);</span><br><span class="line"></span><br><span class="line">                try &#123;</span><br><span class="line">                    <span class="keyword">if</span> (tracing) logger.trace(<span class="string">"start"</span>, <span class="string">"binding to "</span> + jndiUrl);</span><br><span class="line"></span><br><span class="line">                    final Hashtable&lt;?, ?&gt; usemap = EnvHelp.mapToHashtable(attributes);</span><br><span class="line"></span><br><span class="line">                    <span class="built_in">bind</span>(jndiUrl, usemap, objref, rebind);</span><br><span class="line"></span><br><span class="line">                    boundJndiUrl = jndiUrl;</span><br><span class="line">                &#125; catch (NamingException e) &#123;</span><br><span class="line">                    // fit e <span class="keyword">in</span> the nested exception <span class="keyword">if</span> we are on 1.4</span><br><span class="line">                    throw newIOException(<span class="string">"Cannot bind to URL ["</span>+jndiUrl+<span class="string">"]: "</span></span><br><span class="line">                                         + e, e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                // <span class="keyword">if</span> jndiURL is null, we must encode the stub into the URL.</span><br><span class="line">                <span class="keyword">if</span> (tracing) logger.trace(<span class="string">"start"</span>, <span class="string">"Encoding URL"</span>);</span><br><span class="line"></span><br><span class="line">                encodeStubInAddress(objref, attributes);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (tracing) logger.trace(<span class="string">"start"</span>, <span class="string">"Encoded URL: "</span> + this.address);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; catch (Exception e) &#123;</span><br><span class="line">            try &#123;</span><br><span class="line">                rmiServer.close();</span><br><span class="line">            &#125; catch (Exception x) &#123;</span><br><span class="line">                // OK: we are already throwing another exception</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (e instanceof RuntimeException)</span><br><span class="line">                throw (RuntimeException) e;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (e instanceof IOException)</span><br><span class="line">                throw (IOException) e;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                throw newIOException(<span class="string">"Got unexpected exception while "</span> +</span><br><span class="line">                                     <span class="string">"starting the connector server: "</span></span><br><span class="line">                                     + e, e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        rmiServerImpl = rmiServer;</span><br><span class="line"></span><br><span class="line">        synchronized(openedServers) &#123;</span><br><span class="line">            openedServers.add(this);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        state = STARTED;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (tracing) &#123;</span><br><span class="line">            logger.trace(<span class="string">"start"</span>, <span class="string">"Connector Server Address = "</span> + address);</span><br><span class="line">            logger.trace(<span class="string">"start"</span>, <span class="string">"started."</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>前面讲过rmiServerImpl成员是空的，所以这时会调用newServer方法进行创建<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">RMIServerImpl newServer() throws IOException &#123;</span><br><span class="line">    final boolean iiop = isIiopURL(address,<span class="literal">true</span>);</span><br><span class="line">    final int port;</span><br><span class="line">    <span class="keyword">if</span> (address == null)</span><br><span class="line">        port = 0;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        port = address.getPort();</span><br><span class="line">    <span class="keyword">if</span> (iiop)</span><br><span class="line">        <span class="built_in">return</span> newIIOPServer(attributes);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">return</span> newJRMPServer(attributes, port);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>接下来会调用objectToBind<br>通过toStub方法创建当前的RMIServer对象的远程代理对象<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">private static RMIServer objectToBind(</span><br><span class="line">           RMIServerImpl rmiServer, Map&lt;String, ?&gt; env)</span><br><span class="line">       throws IOException &#123;</span><br><span class="line">       <span class="built_in">return</span> RMIConnector.</span><br><span class="line">           connectStub((RMIServer)rmiServer.toStub(),env);</span><br><span class="line">   &#125;</span><br><span class="line">public Remote toStub() throws IOException &#123;</span><br><span class="line">       <span class="built_in">return</span> RemoteObject.toStub(this);</span><br><span class="line">   &#125;</span><br><span class="line">public static Remote toStub(Remote obj) throws NoSuchObjectException &#123;</span><br><span class="line">       <span class="keyword">if</span> (obj instanceof RemoteStub ||</span><br><span class="line">           (obj != null &amp;&amp;</span><br><span class="line">            Proxy.isProxyClass(obj.getClass()) &amp;&amp;</span><br><span class="line">            Proxy.getInvocationHandler(obj) instanceof</span><br><span class="line">            RemoteObjectInvocationHandler))</span><br><span class="line">       &#123;</span><br><span class="line">           <span class="built_in">return</span> obj;</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           <span class="built_in">return</span> sun.rmi.transport.ObjectTable.getStub(obj);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>然后调用bind方法将RMIServer的远程代理对象注册到注册中心<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">void <span class="built_in">bind</span>(String jndiUrl, Hashtable&lt;?, ?&gt; attributes,</span><br><span class="line">             RMIServer rmiServer, boolean rebind)</span><br><span class="line">       throws NamingException, MalformedURLException &#123;</span><br><span class="line">       // <span class="keyword">if</span> jndiURL is not null, we nust <span class="built_in">bind</span> the stub to a</span><br><span class="line">       // directory.</span><br><span class="line">       InitialContext ctx =</span><br><span class="line">           new InitialContext(attributes);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (rebind)</span><br><span class="line">           ctx.rebind(jndiUrl, rmiServer);</span><br><span class="line">       <span class="keyword">else</span></span><br><span class="line">           ctx.bind(jndiUrl, rmiServer);</span><br><span class="line">       ctx.close();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>至此，完成了JmxConnectorServer的启动，程序可以通过jmx规范，访问MBeanServer与其上的MBean<br>关于remote和rmi的细节可以参考本人另一篇博客——java-rmi源码解析<a href="https://blog.csdn.net/weixin_44627989/article/details/93055791" target="_blank" rel="noopener">java-rmi源码解析</a></p>
<h3 id="更多精彩内容"><a href="#更多精彩内容" class="headerlink" title="更多精彩内容"></a>更多精彩内容</h3><h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-java-rmi源码解析" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/06/20/java-rmi源码解析/" class="article-date">
  	<time datetime="2019-06-20T07:08:18.101Z" itemprop="datePublished">2019-06-20</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/06/20/java-rmi源码解析/">
        java-rmi源码解析
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第二十一篇博客–java-rmi源码解析</p>
<h2 id="相关核心类"><a href="#相关核心类" class="headerlink" title="相关核心类"></a>相关核心类</h2><p>sun.rmi.server.UnicastServerRef<br>sun.rmi.server.UnicastRef<br>sun.rmi.server.Util<br>sun.rmi.transport.tcp.TCPEndpoint<br>sun.rmi.transport.LiveRef<br>java.rmi.Naming<br>sun.rmi.registry.RegistryImpl</p>
<h2 id="rmi"><a href="#rmi" class="headerlink" title="rmi"></a>rmi</h2><p>RMI是Java的一组拥护开发分布式应用程序的API。<br>RMI使用Java语言接口定义了远程对象，它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。<br>简单地说，这样使原先的程序在同一操作系统的方法调用，变成了不同操作系统之间程序的方法调用，由于J2EE是分布式程序平台，它以RMI机制实现程序组件在不同操作系统之间的通信。<br>比如，一个EJB可以通过RMI调用Web上另一台机器上的EJB远程方法。</p>
<h2 id="简单使用rmi"><a href="#简单使用rmi" class="headerlink" title="简单使用rmi"></a>简单使用rmi</h2><h3 id="要发布的服务接口"><a href="#要发布的服务接口" class="headerlink" title="要发布的服务接口"></a>要发布的服务接口</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">public interface HelloService extends Remote&#123;</span><br><span class="line">	public String sayHello() throws RemoteException;</span><br><span class="line">	//要发布的服务类的方法必须都throws RemoteException</span><br><span class="line">	//在Util中，创建代理对象时会checkMethod，存在没有throws RemoteException的则抛出IllegalArgumentException</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>util中的checkMethod<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"> public static Remote createProxy(Class&lt;?&gt; implClass,</span><br><span class="line">                                   RemoteRef clientRef,</span><br><span class="line">                                   boolean forceStubUse)</span><br><span class="line">      throws StubNotFoundException</span><br><span class="line">  &#123;</span><br><span class="line">      .....</span><br><span class="line">      final Class&lt;?&gt;[] interfaces = getRemoteInterfaces(implClass);</span><br><span class="line">      .....</span><br><span class="line">  &#125;</span><br><span class="line">  private static Class&lt;?&gt;[] getRemoteInterfaces(Class&lt;?&gt; remoteClass) &#123;</span><br><span class="line">      ArrayList&lt;Class&lt;?&gt;&gt; list = new ArrayList&lt;&gt;();</span><br><span class="line">      getRemoteInterfaces(list, remoteClass);</span><br><span class="line">      <span class="built_in">return</span> list.toArray(new Class&lt;?&gt;[list.size()]);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  private static void getRemoteInterfaces(ArrayList&lt;Class&lt;?&gt;&gt; list, Class&lt;?&gt; cl) &#123;</span><br><span class="line">.....</span><br><span class="line">      <span class="keyword">for</span> (int i = 0; i &lt; interfaces.length; i++) &#123;</span><br><span class="line">          Class&lt;?&gt; intf = interfaces[i];</span><br><span class="line">          <span class="keyword">if</span> (Remote.class.isAssignableFrom(intf)) &#123;</span><br><span class="line">              <span class="keyword">if</span> (!(list.contains(intf))) &#123;</span><br><span class="line">                  Method[] methods = intf.getMethods();</span><br><span class="line">                  <span class="keyword">for</span> (int j = 0; j &lt; methods.length; j++) &#123;</span><br><span class="line">                      checkMethod(methods[j]);</span><br><span class="line">                  &#125;</span><br><span class="line">                  list.add(intf);</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  private static void checkMethod(Method m) &#123;</span><br><span class="line">      Class&lt;?&gt;[] ex = m.getExceptionTypes();</span><br><span class="line">      <span class="keyword">for</span> (int i = 0; i &lt; ex.length; i++) &#123;</span><br><span class="line">          <span class="keyword">if</span> (ex[i].isAssignableFrom(RemoteException.class))</span><br><span class="line">              <span class="built_in">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      throw new IllegalArgumentException(</span><br><span class="line">          <span class="string">"illegal remote method encountered: "</span> + m);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="要发布的服务接口的实现类"><a href="#要发布的服务接口的实现类" class="headerlink" title="要发布的服务接口的实现类"></a>要发布的服务接口的实现类</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">public class HelloServiceImpl extends UnicastRemoteObject implements HelloService &#123;</span><br><span class="line">	//需要继承UnicastRemoteObject，因为Remote只是接口，UnicastRemoteObject是Remote的实现类</span><br><span class="line"></span><br><span class="line">	protected HelloServiceImpl() throws RemoteException &#123;</span><br><span class="line">		super();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	public String sayHello() throws RemoteException&#123;</span><br><span class="line">		<span class="built_in">return</span> <span class="string">"hello"</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="服务发布端与客户消费端"><a href="#服务发布端与客户消费端" class="headerlink" title="服务发布端与客户消费端"></a>服务发布端与客户消费端</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">public class Server &#123;</span><br><span class="line">	final static String host = <span class="string">"127.0.0.1"</span>;</span><br><span class="line">	final static int port = 8080;</span><br><span class="line">	public static void main(String[] args) throws RemoteException, MalformedURLException &#123;</span><br><span class="line">		HelloService helloService = new HelloServiceImpl();</span><br><span class="line">		LocateRegistry.createRegistry(port);</span><br><span class="line">		Naming.rebind(<span class="string">"rmi://"</span>+host+<span class="string">":"</span>+port+<span class="string">"/hello"</span>, helloService);//不写port默认是1099</span><br><span class="line">		System.out.println(<span class="string">"服务启动..."</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">public class Client &#123;</span><br><span class="line"></span><br><span class="line">	public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException &#123;</span><br><span class="line">		HelloService helloService = (HelloService) Naming.lookup(<span class="string">"rmi://"</span>+Server.host+<span class="string">":"</span>+Server.port+<span class="string">"/hello"</span>);//默认端口1099</span><br><span class="line">		System.out.println(helloService.sayHello());</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="追踪源码"><a href="#追踪源码" class="headerlink" title="追踪源码"></a>追踪源码</h2><p>启动服务前，创建服务对象会调用父类无参构造<br>HelloServiceImpl<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">protected HelloServiceImpl() throws RemoteException &#123;</span><br><span class="line">		super();</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure></p>
<p>UnicastRemoteObject构造<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">protected UnicastRemoteObject() throws RemoteException</span><br><span class="line">   &#123;</span><br><span class="line">       this(0);</span><br><span class="line">   &#125;</span><br><span class="line"> protected UnicastRemoteObject(int port) throws RemoteException</span><br><span class="line">   &#123;</span><br><span class="line">       this.port = port;</span><br><span class="line">       exportObject((Remote) this, port);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>在构造中调用了exportObject，将服务暴露出去<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">public static Remote exportObject(Remote obj, int port)</span><br><span class="line">       throws RemoteException</span><br><span class="line">   &#123;</span><br><span class="line">       <span class="built_in">return</span> exportObject(obj, new UnicastServerRef(port));</span><br><span class="line">   &#125;</span><br><span class="line">private static Remote exportObject(Remote obj, UnicastServerRef sref)</span><br><span class="line">       throws RemoteException</span><br><span class="line">   &#123;</span><br><span class="line">       <span class="keyword">if</span> (obj instanceof UnicastRemoteObject) &#123;</span><br><span class="line">           ((UnicastRemoteObject) obj).ref = sref;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="built_in">return</span> sref.exportObject(obj, null, <span class="literal">false</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>构造UnicastServerRef服务器对象来发布服务<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">public UnicastServerRef(int port) &#123;</span><br><span class="line">       super(new LiveRef(port));</span><br><span class="line">       this.filter = null;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>UnicastServerRef调用父类带参构造<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">public UnicastRef(LiveRef liveRef) &#123;</span><br><span class="line">       ref = liveRef;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>sref对象发布服务细节<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">public Remote exportObject(Remote impl, Object data,</span><br><span class="line">                              boolean permanent)</span><br><span class="line">       throws RemoteException</span><br><span class="line">   &#123;</span><br><span class="line">       Class&lt;?&gt; implClass = impl.getClass();</span><br><span class="line">       Remote stub;</span><br><span class="line"></span><br><span class="line">       try &#123;</span><br><span class="line">           stub = Util.createProxy(implClass, getClientRef(), forceStubUse);</span><br><span class="line">       &#125; catch (IllegalArgumentException e) &#123;</span><br><span class="line">           throw new ExportException(</span><br><span class="line">               <span class="string">"remote object implements illegal remote interface"</span>, e);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">if</span> (stub instanceof RemoteStub) &#123;</span><br><span class="line">           setSkeleton(impl);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       Target target =</span><br><span class="line">           new Target(impl, this, stub, ref.getObjID(), permanent);</span><br><span class="line">       ref.exportObject(target);</span><br><span class="line">       hashToMethod_Map = hashToMethod_Maps.get(implClass);</span><br><span class="line">       <span class="built_in">return</span> stub;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>创建代理对象，并生成一个可以真正发布的target，调用ref对象（上一步构建以后传递到父类构造函数的LiveRef）发布出去<br>LiveRef细节<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">public LiveRef(int port) &#123;</span><br><span class="line">       this((new ObjID()), port);</span><br><span class="line">   &#125;</span><br><span class="line">public LiveRef(ObjID objID, int port) &#123;</span><br><span class="line">       this(objID, TCPEndpoint.getLocalEndpoint(port), <span class="literal">true</span>);</span><br><span class="line">   &#125;</span><br><span class="line">public LiveRef(ObjID objID, Endpoint endpoint, boolean isLocal) &#123;</span><br><span class="line">       ep = endpoint;</span><br><span class="line">       id = objID;</span><br><span class="line">       this.isLocal = isLocal;</span><br><span class="line">   &#125;</span><br><span class="line">public void exportObject(Target target) throws RemoteException &#123;</span><br><span class="line">       ep.exportObject(target);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>TCPEndpoint.getLocalEndpoint<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line">public static TCPEndpoint getLocalEndpoint(int port) &#123;</span><br><span class="line">       <span class="built_in">return</span> getLocalEndpoint(port, null, null);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   public static TCPEndpoint getLocalEndpoint(int port,</span><br><span class="line">                                              RMIClientSocketFactory csf,</span><br><span class="line">                                              RMIServerSocketFactory ssf)</span><br><span class="line">   &#123;</span><br><span class="line">       /*</span><br><span class="line">        * Find mapping <span class="keyword">for</span> an endpoint key to the list of <span class="built_in">local</span> unique</span><br><span class="line">        * endpoints <span class="keyword">for</span> this client/server socket factory pair (perhaps</span><br><span class="line">        * null) <span class="keyword">for</span> the specific port.</span><br><span class="line">        */</span><br><span class="line">       TCPEndpoint ep = null;</span><br><span class="line"></span><br><span class="line">       synchronized (localEndpoints) &#123;</span><br><span class="line">           TCPEndpoint endpointKey = new TCPEndpoint(null, port, csf, ssf);</span><br><span class="line">           LinkedList&lt;TCPEndpoint&gt; epList = localEndpoints.get(endpointKey);</span><br><span class="line">           String localHost = resampleLocalHost();</span><br><span class="line"></span><br><span class="line">           <span class="keyword">if</span> (epList == null) &#123;</span><br><span class="line">               /*</span><br><span class="line">                * Create new endpoint list.</span><br><span class="line">                */</span><br><span class="line">               ep = new TCPEndpoint(localHost, port, csf, ssf);</span><br><span class="line">               epList = new LinkedList&lt;TCPEndpoint&gt;();</span><br><span class="line">               epList.add(ep);</span><br><span class="line">               ep.listenPort = port;</span><br><span class="line">               ep.transport = new TCPTransport(epList);</span><br><span class="line">               localEndpoints.put(endpointKey, epList);</span><br><span class="line"></span><br><span class="line">               <span class="keyword">if</span> (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) &#123;</span><br><span class="line">                   TCPTransport.tcpLog.log(Log.BRIEF,</span><br><span class="line">                       <span class="string">"created local endpoint for socket factory "</span> + ssf +</span><br><span class="line">                       <span class="string">" on port "</span> + port);</span><br><span class="line">               &#125;</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">               synchronized (epList) &#123;</span><br><span class="line">                   ep = epList.getLast();</span><br><span class="line">                   String lastHost = ep.host;</span><br><span class="line">                   int lastPort =  ep.port;</span><br><span class="line">                   TCPTransport lastTransport = ep.transport;</span><br><span class="line">                   // assert (localHost == null ^ lastHost != null)</span><br><span class="line">                   <span class="keyword">if</span> (localHost != null &amp;&amp; !localHost.equals(lastHost)) &#123;</span><br><span class="line">                       /*</span><br><span class="line">                        * Hostname has been updated; add updated endpoint</span><br><span class="line">                        * to list.</span><br><span class="line">                        */</span><br><span class="line">                       <span class="keyword">if</span> (lastPort != 0) &#123;</span><br><span class="line">                           /*</span><br><span class="line">                            * Remove outdated endpoints only <span class="keyword">if</span> the</span><br><span class="line">                            * port has already been <span class="built_in">set</span> on those endpoints.</span><br><span class="line">                            */</span><br><span class="line">                           epList.clear();</span><br><span class="line">                       &#125;</span><br><span class="line">                       ep = new TCPEndpoint(localHost, lastPort, csf, ssf);</span><br><span class="line">                       ep.listenPort = port;</span><br><span class="line">                       ep.transport = lastTransport;</span><br><span class="line">                       epList.add(ep);</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="built_in">return</span> ep;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>最终获得一个可以发布服务的TCPEndpoint对象，并调用该对象把服务暴露出去</p>
<h2 id="服务注册与拉取源码分析"><a href="#服务注册与拉取源码分析" class="headerlink" title="服务注册与拉取源码分析"></a>服务注册与拉取源码分析</h2><p>注册服务<br>LocateRegistry.createRegistry(port);<br>创建注册中心时，会创建一个RegistryImpl对象<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">public static Registry createRegistry(int port) throws RemoteException &#123;</span><br><span class="line">       <span class="built_in">return</span> new RegistryImpl(port);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>RegistryImpl代表注册中心<br>RegistryImpl中使用一个HashTable来注册（bind）服务和查找(lookup)服务<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line">private Hashtable&lt;String, Remote&gt; bindings = new Hashtable&lt;&gt;(101);</span><br><span class="line">public RegistryImpl(int port)</span><br><span class="line">       throws RemoteException</span><br><span class="line">   &#123;</span><br><span class="line">       <span class="keyword">if</span> (port == Registry.REGISTRY_PORT &amp;&amp; System.getSecurityManager() != null) &#123;</span><br><span class="line">           try &#123;</span><br><span class="line">               AccessController.doPrivileged(new PrivilegedExceptionAction&lt;Void&gt;() &#123;</span><br><span class="line">                   public Void run() throws RemoteException &#123;</span><br><span class="line">                       LiveRef lref = new LiveRef(id, port);</span><br><span class="line">                       setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));</span><br><span class="line">                       <span class="built_in">return</span> null;</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;, null, new SocketPermission(<span class="string">"localhost:"</span>+port, <span class="string">"listen,accept"</span>));</span><br><span class="line">           &#125; catch (PrivilegedActionException pae) &#123;</span><br><span class="line">               throw (RemoteException)pae.getException();</span><br><span class="line">           &#125;</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           LiveRef lref = new LiveRef(id, port);</span><br><span class="line">           setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line">private void setup(UnicastServerRef uref)</span><br><span class="line">       throws RemoteException</span><br><span class="line">   &#123;</span><br><span class="line">       ref = uref;</span><br><span class="line">       uref.exportObject(this, null, <span class="literal">true</span>);</span><br><span class="line">   &#125;</span><br><span class="line">public Remote lookup(String name)</span><br><span class="line">       throws RemoteException, NotBoundException</span><br><span class="line">   &#123;</span><br><span class="line">       synchronized (bindings) &#123;</span><br><span class="line">           Remote obj = bindings.get(name);</span><br><span class="line">           <span class="keyword">if</span> (obj == null)</span><br><span class="line">               throw new NotBoundException(name);</span><br><span class="line">           <span class="built_in">return</span> obj;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   public void <span class="built_in">bind</span>(String name, Remote obj)</span><br><span class="line">       throws RemoteException, AlreadyBoundException, AccessException</span><br><span class="line">   &#123;</span><br><span class="line">       synchronized (bindings) &#123;</span><br><span class="line">           Remote curr = bindings.get(name);</span><br><span class="line">           <span class="keyword">if</span> (curr != null)</span><br><span class="line">               throw new AlreadyBoundException(name);</span><br><span class="line">           bindings.put(name, obj);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line">public void unbind(String name)</span><br><span class="line">       throws RemoteException, NotBoundException, AccessException</span><br><span class="line">   &#123;</span><br><span class="line">       synchronized (bindings) &#123;</span><br><span class="line">           Remote obj = bindings.get(name);</span><br><span class="line">           <span class="keyword">if</span> (obj == null)</span><br><span class="line">               throw new NotBoundException(name);</span><br><span class="line">           bindings.remove(name);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   public void rebind(String name, Remote obj)</span><br><span class="line">       throws RemoteException, AccessException</span><br><span class="line">   &#123;</span><br><span class="line">       bindings.put(name, obj);</span><br><span class="line">   &#125;</span><br><span class="line">.....</span><br></pre></td></tr></table></figure></p>
<p>最终和服务端暴露服务类似，会把RegistryImpl对象暴露出去</p>
<p>当服务提供方调用Naming.rebind(“rmi://“+host+”:”+port+”/hello”, helloService);注册服务时<br>会先根据端口获取到暴露的注册中心对象RegistryImpl，然后调用其方法注册<br>对应的客户端获取服务的过程也是类似(HelloService) Naming.lookup(“rmi://“+Server.host+”:”+Server.port+”/hello”);<br>会先根据端口获取到暴露的注册中心对象RegistryImpl，然后调用其方法获取<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">public static void rebind(String name, Remote obj)</span><br><span class="line">       throws RemoteException, java.net.MalformedURLException</span><br><span class="line">   &#123;</span><br><span class="line">       ParsedNamingURL parsed = parseURL(name);</span><br><span class="line">       Registry registry = getRegistry(parsed);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (obj == null)</span><br><span class="line">           throw new NullPointerException(<span class="string">"cannot bind to null"</span>);</span><br><span class="line"></span><br><span class="line">       registry.rebind(parsed.name, obj);</span><br><span class="line">   &#125;</span><br><span class="line">public static Remote lookup(String name)</span><br><span class="line">       throws NotBoundException,</span><br><span class="line">           java.net.MalformedURLException,</span><br><span class="line">           RemoteException</span><br><span class="line">   &#123;</span><br><span class="line">       ParsedNamingURL parsed = parseURL(name);</span><br><span class="line">       Registry registry = getRegistry(parsed);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (parsed.name == null)</span><br><span class="line">           <span class="built_in">return</span> registry;</span><br><span class="line">       <span class="built_in">return</span> registry.lookup(parsed.name);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="更多精彩内容"><a href="#更多精彩内容" class="headerlink" title="更多精彩内容"></a>更多精彩内容</h3><h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-springCloud的LoadBalance源码分析" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/06/05/springCloud的LoadBalance源码分析/" class="article-date">
  	<time datetime="2019-06-05T07:31:59.979Z" itemprop="datePublished">2019-06-05</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/06/05/springCloud的LoadBalance源码分析/">
        SpringCloud的LoadBalance源码分析
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第二十篇博客–SpringCloud的LoadBalance源码分析</p>
<h2 id="相关核心类"><a href="#相关核心类" class="headerlink" title="相关核心类"></a>相关核心类</h2><p>LoadBalanced<br>LoadBalancerClient<br>LoadBalancerAutoConfiguration<br>LoadBalancerInterceptor<br>RibbonAutoConfiguration<br>RibbonClassesConditions<br>AllNestedConditions<br>AbstractNestedCondition<br>AbstractNestedCondition.MemberMatchOutcomes<br>AbstractNestedCondition.MemberConditions<br>SpringBootCondition<br>ConditionOutcome</p>
<h2 id="LoadBalanced注解与功能"><a href="#LoadBalanced注解与功能" class="headerlink" title="LoadBalanced注解与功能"></a>LoadBalanced注解与功能</h2><p>通过在创建的RestTemplate上加入LoadBalanced注解，则该RestTemplate会自动成为负载均衡的RestTeplate，请求的url的hostname会作为服务名称去解析<br>用法如下<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">@LoadBalanced</span><br><span class="line">@Bean</span><br><span class="line">   public RestTemplate <span class="function"><span class="title">restTemplate</span></span>() &#123;</span><br><span class="line">       <span class="built_in">return</span> new RestTemplate();</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">public String <span class="function"><span class="title">requestTest</span></span>()&#123;</span><br><span class="line">String url = <span class="string">"http://8781-eurekaClient/getClient2Info"</span>;</span><br><span class="line"><span class="built_in">return</span> restTemplate.getForObject(url, String.class);</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure></p>
<p>等价于以下写法<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">@Autowired</span><br><span class="line">	RestTemplate restTemplate;</span><br><span class="line">	</span><br><span class="line">	@Autowired</span><br><span class="line">    private LoadBalancerClient client;</span><br><span class="line">	</span><br><span class="line">	@RequestMapping(<span class="string">"/getProviderInfo"</span>)</span><br><span class="line">	public String <span class="function"><span class="title">getProviderInfo</span></span>() &#123;</span><br><span class="line">		ServiceInstance instance = client.choose(<span class="string">"8780-eurekaClient"</span>);</span><br><span class="line">        String url = <span class="string">"http://"</span> + instance.getHost() + <span class="string">":"</span> + instance.getPort() + <span class="string">"/getInfo"</span>;</span><br><span class="line">        <span class="built_in">return</span> restTemplate.getForObject(url, String.class);</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h2><p>1.LoadBalanced注解<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">@Target(&#123; ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD &#125;)</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">@Documented</span><br><span class="line">@Inherited</span><br><span class="line">@Qualifier</span><br><span class="line">public @interface LoadBalanced &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>这里注意，该注解被标记了@Qualifier注解，后续使用@Autowired注解注入时，如果注解的属性也被LoadBalanced注解，则只装载一样被LoadBalanced注解的符合autowired的对象<br>2.LoadBalnceAutoConfigure<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line">@Configuration</span><br><span class="line">@ConditionalOnClass(RestTemplate.class)</span><br><span class="line">@ConditionalOnBean(LoadBalancerClient.class)</span><br><span class="line">@EnableConfigurationProperties(LoadBalancerRetryProperties.class)</span><br><span class="line">public class LoadBalancerAutoConfiguration &#123;</span><br><span class="line"></span><br><span class="line">	@LoadBalanced</span><br><span class="line">	@Autowired(required = <span class="literal">false</span>)</span><br><span class="line">	private List&lt;RestTemplate&gt; restTemplates = Collections.emptyList();</span><br><span class="line"></span><br><span class="line">	@Autowired(required = <span class="literal">false</span>)</span><br><span class="line">	private List&lt;LoadBalancerRequestTransformer&gt; transformers = Collections.emptyList();</span><br><span class="line"></span><br><span class="line">	@Bean</span><br><span class="line">	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(</span><br><span class="line">			final ObjectProvider&lt;List&lt;RestTemplateCustomizer&gt;&gt; restTemplateCustomizers) &#123;</span><br><span class="line">		<span class="built_in">return</span> () -&gt; restTemplateCustomizers.ifAvailable(customizers -&gt; &#123;</span><br><span class="line">			<span class="keyword">for</span> (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) &#123;</span><br><span class="line">				<span class="keyword">for</span> (RestTemplateCustomizer customizer : customizers) &#123;</span><br><span class="line">					customizer.customize(restTemplate);</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Bean</span><br><span class="line">	@ConditionalOnMissingBean</span><br><span class="line">	public LoadBalancerRequestFactory loadBalancerRequestFactory(</span><br><span class="line">			LoadBalancerClient loadBalancerClient) &#123;</span><br><span class="line">		<span class="built_in">return</span> new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Configuration</span><br><span class="line">	@ConditionalOnMissingClass(<span class="string">"org.springframework.retry.support.RetryTemplate"</span>)</span><br><span class="line">	static class LoadBalancerInterceptorConfig &#123;</span><br><span class="line"></span><br><span class="line">		@Bean</span><br><span class="line">		public LoadBalancerInterceptor ribbonInterceptor(</span><br><span class="line">				LoadBalancerClient loadBalancerClient,</span><br><span class="line">				LoadBalancerRequestFactory requestFactory) &#123;</span><br><span class="line">			<span class="built_in">return</span> new LoadBalancerInterceptor(loadBalancerClient, requestFactory);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		@Bean</span><br><span class="line">		@ConditionalOnMissingBean</span><br><span class="line">		public RestTemplateCustomizer restTemplateCustomizer(</span><br><span class="line">				final LoadBalancerInterceptor loadBalancerInterceptor) &#123;</span><br><span class="line">			<span class="built_in">return</span> restTemplate -&gt; &#123;</span><br><span class="line">				List&lt;ClientHttpRequestInterceptor&gt; list = new ArrayList&lt;&gt;(</span><br><span class="line">						restTemplate.getInterceptors());</span><br><span class="line">				list.add(loadBalancerInterceptor);</span><br><span class="line">				restTemplate.setInterceptors(list);</span><br><span class="line">			&#125;;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">	......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>@ConditionalOnClass(RestTemplate.class)<br>@ConditionalOnBean(LoadBalancerClient.class)<br>这个配置类依赖于加载RestTemplate类以及创建了LoadBalancerClient对象<br>其中会创建一个RestTemplateCustomizer提供给上方创建智能初始化的bean，该bean初始化过程会使用这个RestTemplateCustomizer，为每个loadBalanced注解的restTemplate加上一个负载均衡的拦截器<br>如果配置生效，根据前面对LoadBalanced注解了解，此处的集合成员restTemplates就是IOC容器中，被LoadBalanced注解了的RestTemplate<br>RestTemplate是我们手动的导入，接下来查找LoadBalancerClient对象，在RibbonAutoConfiguration中<br>拦截器<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor &#123;</span><br><span class="line"></span><br><span class="line">	private LoadBalancerClient loadBalancer;</span><br><span class="line"></span><br><span class="line">	private LoadBalancerRequestFactory requestFactory;</span><br><span class="line"></span><br><span class="line">	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) &#123;</span><br><span class="line">		// <span class="keyword">for</span> backwards compatibility</span><br><span class="line">		this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,</span><br><span class="line">			final ClientHttpRequestExecution execution) throws IOException &#123;</span><br><span class="line">		final URI originalUri = request.getURI();</span><br><span class="line">		String serviceName = originalUri.getHost();</span><br><span class="line">		Assert.state(serviceName != null,</span><br><span class="line">				<span class="string">"Request URI does not contain a valid hostname: "</span> + originalUri);</span><br><span class="line">		<span class="built_in">return</span> this.loadBalancer.execute(serviceName,</span><br><span class="line">				this.requestFactory.createRequest(request, body, execution));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>Ribbon自动配置<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">@Configuration</span><br><span class="line">@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)</span><br><span class="line">@RibbonClients</span><br><span class="line">@AutoConfigureAfter(name = <span class="string">"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"</span>)</span><br><span class="line">@AutoConfigureBefore(&#123; LoadBalancerAutoConfiguration.class,</span><br><span class="line">		AsyncLoadBalancerAutoConfiguration.class &#125;)</span><br><span class="line">@EnableConfigurationProperties(&#123; RibbonEagerLoadProperties.class,</span><br><span class="line">		ServerIntrospectorProperties.class &#125;)</span><br><span class="line">public class RibbonAutoConfiguration &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	@Bean</span><br><span class="line">	@ConditionalOnMissingBean(LoadBalancerClient.class)</span><br><span class="line">	public LoadBalancerClient <span class="function"><span class="title">loadBalancerClient</span></span>() &#123;</span><br><span class="line">		<span class="built_in">return</span> new RibbonLoadBalancerClient(springClientFactory());</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	/**</span><br><span class="line">	 * &#123;@link AllNestedConditions&#125; that checks that either multiple classes are present.</span><br><span class="line">	 */</span><br><span class="line">	static class RibbonClassesConditions extends AllNestedConditions &#123;</span><br><span class="line"></span><br><span class="line">		<span class="function"><span class="title">RibbonClassesConditions</span></span>() &#123;</span><br><span class="line">			super(ConfigurationPhase.PARSE_CONFIGURATION);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		@ConditionalOnClass(IClient.class)</span><br><span class="line">		static class IClientPresent &#123;</span><br><span class="line"></span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		@ConditionalOnClass(RestTemplate.class)</span><br><span class="line">		static class RestTemplatePresent &#123;</span><br><span class="line"></span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		@ConditionalOnClass(AsyncRestTemplate.class)</span><br><span class="line">		static class AsyncRestTemplatePresent &#123;</span><br><span class="line"></span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		@ConditionalOnClass(Ribbon.class)</span><br><span class="line">		static class RibbonPresent &#123;</span><br><span class="line"></span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>当IOC容器没有LoadBalancerClient实例时，RibbonAutoConfiguration会自动创建一个Ribbon实现的负载均衡客户端，是其子类<br>这也是为什么springCloud的Eureka客户端会默认使用Ribbon作为负载均衡<br>因为spring-cloud-starter-netflix-eureka-client工程引入依赖了spring-cloud-starter-netflix-ribbon</p>
<p>@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)的意思是该要求满足该类中所有condition相关注解(程序加载了对应的四个类)<br>public abstract class AllNestedConditions extends AbstractNestedCondition<br>public abstract class AbstractNestedCondition extends SpringBootCondition implements ConfigurationCondition<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line">public abstract class SpringBootCondition implements Condition &#123;</span><br><span class="line"></span><br><span class="line">	private final Log logger = LogFactory.getLog(getClass());</span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	public final boolean matches(ConditionContext context,</span><br><span class="line">			AnnotatedTypeMetadata metadata) &#123;</span><br><span class="line">			//成立判断方法</span><br><span class="line">		String classOrMethodName = getClassOrMethodName(metadata);</span><br><span class="line">		try &#123;</span><br><span class="line">			ConditionOutcome outcome = getMatchOutcome(context, metadata);//调用getMatchOutcome</span><br><span class="line">			logOutcome(classOrMethodName, outcome);</span><br><span class="line">			recordEvaluation(context, classOrMethodName, outcome);</span><br><span class="line">			<span class="built_in">return</span> outcome.isMatch();//注意这里，outcome的match属性作为结果</span><br><span class="line">		&#125;</span><br><span class="line">		catch (NoClassDefFoundError ex) &#123;</span><br><span class="line">			throw new IllegalStateException(</span><br><span class="line">					<span class="string">"Could not evaluate condition on "</span> + classOrMethodName + <span class="string">" due to "</span></span><br><span class="line">							+ ex.getMessage() + <span class="string">" not "</span></span><br><span class="line">							+ <span class="string">"found. Make sure your own configuration does not rely on "</span></span><br><span class="line">							+ <span class="string">"that class. This can also happen if you are "</span></span><br><span class="line">							+ <span class="string">"@ComponentScanning a springframework package (e.g. if you "</span></span><br><span class="line">							+ <span class="string">"put a @ComponentScan in the default package by mistake)"</span>,</span><br><span class="line">					ex);</span><br><span class="line">		&#125;</span><br><span class="line">		catch (RuntimeException ex) &#123;</span><br><span class="line">			throw new IllegalStateException(</span><br><span class="line">					<span class="string">"Error processing condition on "</span> + getName(metadata), ex);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	......</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public abstract class AbstractNestedCondition extends SpringBootCondition</span><br><span class="line">		implements ConfigurationCondition &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	public ConditionOutcome getMatchOutcome(ConditionContext context,</span><br><span class="line">			AnnotatedTypeMetadata metadata) &#123;</span><br><span class="line">		String className = getClass().getName();</span><br><span class="line">		MemberConditions memberConditions = new MemberConditions(context, className);</span><br><span class="line">		MemberMatchOutcomes memberOutcomes = new MemberMatchOutcomes(memberConditions);</span><br><span class="line">		<span class="built_in">return</span> getFinalMatchOutcome(memberOutcomes);</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	protected static class MemberMatchOutcomes &#123;</span><br><span class="line"></span><br><span class="line">		private final List&lt;ConditionOutcome&gt; all;</span><br><span class="line"></span><br><span class="line">		private final List&lt;ConditionOutcome&gt; matches;</span><br><span class="line"></span><br><span class="line">		private final List&lt;ConditionOutcome&gt; nonMatches;</span><br><span class="line"></span><br><span class="line">		public MemberMatchOutcomes(MemberConditions memberConditions) &#123;</span><br><span class="line">			this.all = Collections.unmodifiableList(memberConditions.getMatchOutcomes());</span><br><span class="line">			List&lt;ConditionOutcome&gt; matches = new ArrayList&lt;&gt;();</span><br><span class="line">			List&lt;ConditionOutcome&gt; nonMatches = new ArrayList&lt;&gt;();</span><br><span class="line">			<span class="keyword">for</span> (ConditionOutcome outcome : this.all) &#123;</span><br><span class="line">				//是否满足加到不同属性集合</span><br><span class="line">				(outcome.isMatch() ? matches : nonMatches).add(outcome);</span><br><span class="line">			&#125;</span><br><span class="line">			this.matches = Collections.unmodifiableList(matches);</span><br><span class="line">			this.nonMatches = Collections.unmodifiableList(nonMatches);</span><br><span class="line">		&#125;</span><br><span class="line">		......</span><br><span class="line"></span><br><span class="line">	&#125;	</span><br><span class="line">&#125;</span><br><span class="line">public abstract class AllNestedConditions extends AbstractNestedCondition &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) &#123;</span><br><span class="line">		boolean match = hasSameSize(memberOutcomes.getMatches(), memberOutcomes.getAll());</span><br><span class="line">		//match就是判断结果</span><br><span class="line">		List&lt;ConditionMessage&gt; messages = new ArrayList&lt;&gt;();</span><br><span class="line">		messages.add(ConditionMessage.forCondition(<span class="string">"AllNestedConditions"</span>)</span><br><span class="line">				.because(memberOutcomes.getMatches().size() + <span class="string">" matched "</span></span><br><span class="line">						+ memberOutcomes.getNonMatches().size() + <span class="string">" did not"</span>));</span><br><span class="line">		<span class="keyword">for</span> (ConditionOutcome outcome : memberOutcomes.getAll()) &#123;</span><br><span class="line">			messages.add(outcome.getConditionMessage());</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="built_in">return</span> new ConditionOutcome(match, ConditionMessage.of(messages));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	private boolean hasSameSize(List&lt;?&gt; list1, List&lt;?&gt; list2) &#123;</span><br><span class="line">		<span class="built_in">return</span> list1.size() == list2.size();</span><br><span class="line">	&#125;</span><br><span class="line">	......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>ribbon的loadBalanceClient实现类<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br></pre></td><td class="code"><pre><span class="line">public class RibbonLoadBalancerClient implements LoadBalancerClient &#123;</span><br><span class="line"></span><br><span class="line">	private SpringClientFactory clientFactory;</span><br><span class="line"></span><br><span class="line">	public RibbonLoadBalancerClient(SpringClientFactory clientFactory) &#123;</span><br><span class="line">		this.clientFactory = clientFactory;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	public URI reconstructURI(ServiceInstance instance, URI original) &#123;</span><br><span class="line">		Assert.notNull(instance, <span class="string">"instance can not be null"</span>);</span><br><span class="line">		String serviceId = instance.getServiceId();</span><br><span class="line">		RibbonLoadBalancerContext context = this.clientFactory</span><br><span class="line">				.getLoadBalancerContext(serviceId);</span><br><span class="line"></span><br><span class="line">		URI uri;</span><br><span class="line">		Server server;</span><br><span class="line">		<span class="keyword">if</span> (instance instanceof RibbonServer) &#123;</span><br><span class="line">			RibbonServer ribbonServer = (RibbonServer) instance;</span><br><span class="line">			server = ribbonServer.getServer();</span><br><span class="line">			uri = updateToSecureConnectionIfNeeded(original, ribbonServer);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			server = new Server(instance.getScheme(), instance.getHost(),</span><br><span class="line">					instance.getPort());</span><br><span class="line">			IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);</span><br><span class="line">			ServerIntrospector serverIntrospector = serverIntrospector(serviceId);</span><br><span class="line">			uri = updateToSecureConnectionIfNeeded(original, clientConfig,</span><br><span class="line">					serverIntrospector, server);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="built_in">return</span> context.reconstructURIWithServer(server, uri);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	public ServiceInstance choose(String serviceId) &#123;</span><br><span class="line">		<span class="built_in">return</span> choose(serviceId, null);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	/**</span><br><span class="line">	 * New: Select a server using a <span class="string">'key'</span>.</span><br><span class="line">	 * @param serviceId of the service to choose an instance <span class="keyword">for</span></span><br><span class="line">	 * @param hint to specify the service instance</span><br><span class="line">	 * @<span class="built_in">return</span> the selected &#123;@link ServiceInstance&#125;</span><br><span class="line">	 */</span><br><span class="line">	public ServiceInstance choose(String serviceId, Object hint) &#123;</span><br><span class="line">		Server server = getServer(getLoadBalancer(serviceId), hint);</span><br><span class="line">		<span class="keyword">if</span> (server == null) &#123;</span><br><span class="line">			<span class="built_in">return</span> null;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="built_in">return</span> new RibbonServer(serviceId, server, isSecure(server, serviceId),</span><br><span class="line">				serverIntrospector(serviceId).getMetadata(server));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	public &lt;T&gt; T execute(String serviceId, LoadBalancerRequest&lt;T&gt; request)</span><br><span class="line">			throws IOException &#123;</span><br><span class="line">		<span class="built_in">return</span> execute(serviceId, request, null);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	/**</span><br><span class="line">	 * New: Execute a request by selecting server using a <span class="string">'key'</span>. The hint will have to be</span><br><span class="line">	 * the last parameter to not mess with the `execute(serviceId, ServiceInstance,</span><br><span class="line">	 * request)` method. This somewhat breaks the fluent coding style when using a lambda</span><br><span class="line">	 * to define the LoadBalancerRequest.</span><br><span class="line">	 * @param &lt;T&gt; returned request execution result <span class="built_in">type</span></span><br><span class="line">	 * @param serviceId id of the service to execute the request to</span><br><span class="line">	 * @param request to be executed</span><br><span class="line">	 * @param hint used to choose appropriate &#123;@link Server&#125; instance</span><br><span class="line">	 * @<span class="built_in">return</span> request execution result</span><br><span class="line">	 * @throws IOException executing the request may result <span class="keyword">in</span> an &#123;@link IOException&#125;</span><br><span class="line">	 */</span><br><span class="line">	public &lt;T&gt; T execute(String serviceId, LoadBalancerRequest&lt;T&gt; request, Object hint)</span><br><span class="line">			throws IOException &#123;</span><br><span class="line">		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);</span><br><span class="line">		Server server = getServer(loadBalancer, hint);</span><br><span class="line">		<span class="keyword">if</span> (server == null) &#123;</span><br><span class="line">			throw new IllegalStateException(<span class="string">"No instances available for "</span> + serviceId);</span><br><span class="line">		&#125;</span><br><span class="line">		RibbonServer ribbonServer = new RibbonServer(serviceId, server,</span><br><span class="line">				isSecure(server, serviceId),</span><br><span class="line">				serverIntrospector(serviceId).getMetadata(server));</span><br><span class="line"></span><br><span class="line">		<span class="built_in">return</span> execute(serviceId, ribbonServer, request);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Override</span><br><span class="line">	public &lt;T&gt; T execute(String serviceId, ServiceInstance serviceInstance,</span><br><span class="line">			LoadBalancerRequest&lt;T&gt; request) throws IOException &#123;</span><br><span class="line">		Server server = null;</span><br><span class="line">		<span class="keyword">if</span> (serviceInstance instanceof RibbonServer) &#123;</span><br><span class="line">			server = ((RibbonServer) serviceInstance).getServer();</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (server == null) &#123;</span><br><span class="line">			throw new IllegalStateException(<span class="string">"No instances available for "</span> + serviceId);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		RibbonLoadBalancerContext context = this.clientFactory</span><br><span class="line">				.getLoadBalancerContext(serviceId);</span><br><span class="line">		RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);</span><br><span class="line"></span><br><span class="line">		try &#123;</span><br><span class="line">			T returnVal = request.apply(serviceInstance);</span><br><span class="line">			statsRecorder.recordStats(returnVal);</span><br><span class="line">			<span class="built_in">return</span> returnVal;</span><br><span class="line">		&#125;</span><br><span class="line">		// catch IOException and rethrow so RestTemplate behaves correctly</span><br><span class="line">		catch (IOException ex) &#123;</span><br><span class="line">			statsRecorder.recordStats(ex);</span><br><span class="line">			throw ex;</span><br><span class="line">		&#125;</span><br><span class="line">		catch (Exception ex) &#123;</span><br><span class="line">			statsRecorder.recordStats(ex);</span><br><span class="line">			ReflectionUtils.rethrowRuntimeException(ex);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="built_in">return</span> null;</span><br><span class="line">	&#125;</span><br><span class="line">	......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-使用HandlerInterceptor和RateLimiter进行限流" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/05/26/使用HandlerInterceptor和RateLimiter进行限流/" class="article-date">
  	<time datetime="2019-05-26T12:56:27.192Z" itemprop="datePublished">2019-05-26</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/05/26/使用HandlerInterceptor和RateLimiter进行限流/">
        使用HandlerInterceptor和RateLimiter进行限流
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第十九篇博客–使用HandlerInterceptor和RateLimiter进行限流</p>
<h2 id="为什么使用拦截器"><a href="#为什么使用拦截器" class="headerlink" title="为什么使用拦截器"></a>为什么使用拦截器</h2><p>目前网上有博客使用的是aop，但是因为我项目里使用了shiro进行权限验证<br>shiro的过滤器链优先级是低于spring的过滤器的<br>所以根据约定优于配置，直接使用过滤器就不需要去配置shiro的一些映射和过滤器的优先级<br>同时网上博客基于aop为注解添加令牌桶效率的属性是有bug的，因为springMVC多线程是Method级别<br>而在aop的切面里创建rateLimiter或者为拿到的method对象的annotation对象绑定一个rateLimiter都会有线程问题<br>基于以上，把ratelimiter拿到拦截器，如果要定义不同rateLimiter可以改为定义多个拦截器和注解</p>
<h2 id="常见限流算法"><a href="#常见限流算法" class="headerlink" title="常见限流算法"></a>常见限流算法</h2><p>常见的限流方法有信号量计数器（Semaphore）计数当前线程个数<br>漏桶算法—使用一个容器保存进来的任务，按照固定速率流出任务，桶满时新加入的任务直接流出<br>令牌桶算法—使用一个容器存储令牌，按照固定速率生产令牌，令牌桶满时直接丢弃新生成令牌，任务进来以后尝试获取令牌，获取成功的任务开始执行<br>计数器较麻烦，需要维护实时记录线程数，完成时进行维护<br>对比令牌桶和漏桶，漏桶算法无法解决当容量空闲了一段时间以后，大量任务一起进来时，执行的平均效率低下的问题，而令牌桶的令牌生成速率是固定的</p>
<h2 id="rateLimiter介绍"><a href="#rateLimiter介绍" class="headerlink" title="rateLimiter介绍"></a>rateLimiter介绍</h2><p>RateLimiter是guava提供的基于令牌桶算法的实现类<br>create（Double permitsPerSecond）方法根据给定的（令牌:单位时间（1s））比例为令牌生成速率<br>tryAcquire（）方法尝试获取一个令牌，立即返回true/false，不阻塞，重载方法具备设置获取令牌个数、获取最大等待时间等参数<br>acquire（）方法与tryAcquire类似，但是会阻塞，尝试获取一个令牌，没有时则阻塞直到获取成功</p>
<h2 id="注解创建"><a href="#注解创建" class="headerlink" title="注解创建"></a>注解创建</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">@Target(ElementType.METHOD)</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">@Documented</span><br><span class="line">public @interface RateLimit &#123;</span><br><span class="line"> </span><br><span class="line">    /**</span><br><span class="line">     * 获取令牌的等待时间  默认0</span><br><span class="line">     * @<span class="built_in">return</span></span><br><span class="line">     */</span><br><span class="line">	int value() default 0;</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">    /**</span><br><span class="line">     * 超时时间单位</span><br><span class="line">     * @<span class="built_in">return</span></span><br><span class="line">     */</span><br><span class="line">    TimeUnit timeOutUnit() default TimeUnit.MILLISECONDS;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="拦截器"><a href="#拦截器" class="headerlink" title="拦截器"></a>拦截器</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">public class RateLimitInterceptor implements HandlerInterceptor &#123;</span><br><span class="line"> </span><br><span class="line">    private final static Logger logger = LoggerFactory.getLogger(RateLimitInterceptor.class);</span><br><span class="line">    </span><br><span class="line">    //每s产生2000个令牌</span><br><span class="line">    RateLimiter rateLimiter = RateLimiter.create(2000);</span><br><span class="line"> </span><br><span class="line">    @Override</span><br><span class="line">    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,</span><br><span class="line">                             Object handler) throws Exception &#123;</span><br><span class="line">        //如果是SpringMVC请求</span><br><span class="line">        <span class="keyword">if</span>(handler instanceof HandlerMethod)&#123;</span><br><span class="line">            HandlerMethod handlerMethod = (HandlerMethod) handler;</span><br><span class="line">            RateLimit rateLimit = handlerMethod.getMethodAnnotation(RateLimit.class);</span><br><span class="line">            <span class="keyword">if</span>(rateLimit != null) &#123;</span><br><span class="line">            	logger.info(<span class="string">"rateLimit intercept method..."</span>);</span><br><span class="line">            	<span class="keyword">if</span> (!rateLimiter.tryAcquire(rateLimit.value(), rateLimit.timeOutUnit())) &#123;</span><br><span class="line">            		throw new Exception(<span class="string">"系统繁忙，新稍后再试!"</span>);</span><br><span class="line">					//这里抛出异常是因为我项目里对异常进行了全局处理</span><br><span class="line">                 &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="springBoot拦截器配置"><a href="#springBoot拦截器配置" class="headerlink" title="springBoot拦截器配置"></a>springBoot拦截器配置</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">@Configuration</span><br><span class="line">public class WebMVCConfig implements WebMvcConfigurer &#123;</span><br><span class="line">    </span><br><span class="line">    @Override</span><br><span class="line">    public void addInterceptors(InterceptorRegistry registry) &#123;</span><br><span class="line">        registry.addInterceptor(new RateLimitInterceptor()).addPathPatterns(<span class="string">"/**"</span>)</span><br><span class="line">                .excludePathPatterns(<span class="string">"/static/**"</span>, <span class="string">"/"</span>);</span><br><span class="line">				//配置限流拦截器拦截以及不拦截的映射</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="开始使用"><a href="#开始使用" class="headerlink" title="开始使用"></a>开始使用</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">@RateLimit(1000)	//最大等待时间1s</span><br><span class="line">@RequestMapping(<span class="string">"/login"</span>)</span><br><span class="line">public ModelAndView login(User user) &#123;</span><br><span class="line">	ModelAndView mv = new ModelAndView();</span><br><span class="line">	String username=user.getUsername();</span><br><span class="line">	String password=user.getPassword();</span><br><span class="line">	UsernamePasswordToken token = new UsernamePasswordToken(username,password,<span class="literal">false</span>);</span><br><span class="line">	Subject subject = SecurityUtils.getSubject();</span><br><span class="line">	try &#123;</span><br><span class="line">		subject.login(token);</span><br><span class="line">	&#125; catch(IncorrectCredentialsException e)&#123;</span><br><span class="line">		mv.addObject(<span class="string">"msg"</span>, <span class="string">"密码错误"</span>);</span><br><span class="line">		mv.setViewName(<span class="string">"/login.html"</span>);</span><br><span class="line">		<span class="built_in">return</span> mv;</span><br><span class="line">	&#125; catch (AuthenticationException e) &#123;</span><br><span class="line">		mv.addObject(<span class="string">"msg"</span>, <span class="string">"登录失败"</span>);</span><br><span class="line">		mv.setViewName(<span class="string">"/login.html"</span>);</span><br><span class="line">		<span class="built_in">return</span> mv;</span><br><span class="line">	&#125;		</span><br><span class="line">	</span><br><span class="line">	mv.addObject(<span class="string">"userDto"</span>,(UserDto)subject.getSession().getAttribute(<span class="string">"userDto"</span>));</span><br><span class="line">	mv.setViewName(<span class="string">"/index.html"</span>);</span><br><span class="line">	<span class="built_in">return</span> mv;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
    <article id="post-字符串常量池在1.6与1.8以及String、StringBuffer、StringBuilder" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/blog/2019/04/12/字符串常量池在1.6与1.8以及String、StringBuffer、StringBuilder/" class="article-date">
  	<time datetime="2019-04-12T03:09:49.400Z" itemprop="datePublished">2019-04-12</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/blog/2019/04/12/字符串常量池在1.6与1.8以及String、StringBuffer、StringBuilder/">
        字符串常量池在1.6与1.8以及String、StringBuffer、StringBuilder
        
      </a>
    </h1>
  

      </header>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>欢迎查看Eetal的第十八篇博客–字符串常量池在1.6与1.8以及String、StringBuffer、StringBuilder</p>
<h2 id="字符串加入常量池时机"><a href="#字符串加入常量池时机" class="headerlink" title="字符串加入常量池时机"></a>字符串加入常量池时机</h2><p>直接使用双引号声明出来的String对象会直接存储在常量池中，相同字符串使用同一个对象<br>而使用构造函数创建的会新创建一个字符串对象，但是该字符串对象的字符数组value会引用常量池中的字符串对象的字符数组<br>使用String的concat方法拼接后的字符串使用的是share模式的构造函数进行构造，默认不会引用常量池（此处的share模式有点threadLocal的那种意思，所以是创建副本不去引用常量池）<br>对于不进入常量池而常量池中没有的字符串，可以显示调用String的intern方法将其加入常量池<br>引用自博客<a href="https://blog.csdn.net/chen1280436393/article/details/51768609" target="_blank" rel="noopener">https://blog.csdn.net/chen1280436393/article/details/51768609</a><br>也就是说，代码里双引号直接包含的字符串会被放进常量池<br>比如<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">String str1 = <span class="string">"我爱java"</span>;//我爱java进入常量池</span><br><span class="line">String str2 = <span class="string">"歪歪"</span>+<span class="string">"梯"</span>;//因为编译器会优化，所以等价于<span class="string">"歪歪梯"</span>，歪歪梯会放入常量池</span><br><span class="line">String str3 = new String(<span class="string">"构造1"</span>);//构造1会放入常量池</span><br><span class="line">String str4 = new String(<span class="string">"构造2"</span>)+<span class="string">"测试"</span>;//构造2会放入常量池。测试会放入常量池</span><br><span class="line">String str5 = new String(<span class="string">"构造3"</span>)+new String(<span class="string">"构造4"</span>);//构造3会放入常量池。构造4会放入常量池</span><br><span class="line">/*</span><br><span class="line">*最终常量池包括 我爱java 歪歪梯 构造1 构造2 构造3 构造4 测试 7个字符串</span><br><span class="line">*注意非字面常量字符串拼接的结果字符串默认并不会加入常量池（因此也不会默认去引用常量池，这个下面会验证）</span><br><span class="line">*/</span><br></pre></td></tr></table></figure></p>
<p>如果是直接使用常量字符串赋值的，会直接引用常量池该字符串对象<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">String str = &quot;123&quot;;</span><br><span class="line">String str2 = &quot;1&quot;+&quot;23&quot;;</span><br><span class="line">System.out.println(str == str2);//true</span><br></pre></td></tr></table></figure></p>
<p>要将非字面常量字符串存储进常量池，可以调用字符串对象的intern方法<br>为什么只存储字面常量，其实也不难理解，jvm虚拟机显示存储字面常量是因为字面常量是直接出现在代码里的，是确定，的同时也是一定会用到的，而且有很大概率用来进行多次拼接（循环体代码），所以放进常量池优化内存</p>
<h2 id="jdk1-6-与-jdk1-8的String和常量池"><a href="#jdk1-6-与-jdk1-8的String和常量池" class="headerlink" title="jdk1.6 与 jdk1.8的String和常量池"></a>jdk1.6 与 jdk1.8的String和常量池</h2><p>jdk1.6使用（方法区）永久代存储类的静态信息和运行时常量池，字符串常量池也在其中，因为常量池不在堆内存，只有触发fullgc才会回收常量池中对象</p>
<h3 id="jdk1-6字符串常量池内存示意图"><a href="#jdk1-6字符串常量池内存示意图" class="headerlink" title="jdk1.6字符串常量池内存示意图"></a>jdk1.6字符串常量池内存示意图</h3><p><img src="/blog/images/string/jdk1.6.jpg" alt="jdk1.6字符串常量池内存示意图"></p>
<h3 id="jdk1-8字符串常量池内存示意图"><a href="#jdk1-8字符串常量池内存示意图" class="headerlink" title="jdk1.8字符串常量池内存示意图"></a>jdk1.8字符串常量池内存示意图</h3><p>jdk1.8字符串常量池移到元数据，元数据在本地内存，同时字符串加入常量池行为发生变化<br><img src="/blog/images/string/jdk1.8.jpg" alt="jdk1.8字符串常量池内存示意图"><br>jdk1.6在调用intern时，如果常量池没有执行的操作是复制字符串在常量池创建一个字符串对象，并返回该对象<br>jdk1.8在调用intern时，如果常量池没有则在常量池创建一个字符串引用，引用当前字符串对象，返回this<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">String str1 = new StringBuilder(<span class="string">"hello"</span>).append(<span class="string">"java"</span>).toString();</span><br><span class="line">String str2 = new StringBuilder(<span class="string">"hello"</span>).append(<span class="string">"java"</span>).toString();</span><br><span class="line">//此时常量池只有两个字符串hello和java</span><br><span class="line"></span><br><span class="line">//jdk1.6的测试效果</span><br><span class="line">System.out.println(str1.intern()==str1);//<span class="literal">false</span></span><br><span class="line">System.out.println(str2==str2.intern());//<span class="literal">false</span></span><br><span class="line"></span><br><span class="line">//jdk1.8的测试效果</span><br><span class="line">System.out.println(str1.intern()==str1);//<span class="literal">true</span></span><br><span class="line">System.out.println(str2==str2.intern());//<span class="literal">false</span></span><br></pre></td></tr></table></figure></p>
<h2 id="使用jol工具进行验证"><a href="#使用jol工具进行验证" class="headerlink" title="使用jol工具进行验证"></a>使用jol工具进行验证</h2><h3 id="验证代码"><a href="#验证代码" class="headerlink" title="验证代码"></a>验证代码</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">String str1 = new StringBuilder(<span class="string">"hello"</span>).append(<span class="string">"java"</span>).toString();</span><br><span class="line">String str2 = new StringBuilder(<span class="string">"hello"</span>).append(<span class="string">"java"</span>).toString();</span><br><span class="line"></span><br><span class="line">System.out.println(str1.intern()==str1);</span><br><span class="line">System.out.println(str2==str2.intern());</span><br><span class="line"></span><br><span class="line">System.out.println(<span class="string">"use jol check ..."</span>);</span><br><span class="line"></span><br><span class="line">String builderString = new StringBuilder(<span class="string">"faker"</span>).append(<span class="string">"大魔王"</span>).toString();</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line">System.out.println(<span class="string">"builderString info"</span>);</span><br><span class="line">System.out.println(GraphLayout.parseInstance(builderString).toPrintable());</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line"></span><br><span class="line">String sin = builderString.intern();</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line">System.out.println(<span class="string">"init builderString.intern() info"</span>);</span><br><span class="line">System.out.println(GraphLayout.parseInstance(sin).toPrintable());</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line"></span><br><span class="line">String constStr = <span class="string">"faker大魔王"</span>;</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line">System.out.println(<span class="string">"init String constStr = 'faker大魔王' info"</span>);</span><br><span class="line">System.out.println(GraphLayout.parseInstance(constStr).toPrintable());</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line"></span><br><span class="line">String newString = new String(<span class="string">"faker大魔王"</span>);</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line">System.out.println(<span class="string">"init new String('faker大魔王') info"</span>);</span><br><span class="line">System.out.println(GraphLayout.parseInstance(newString).toPrintable());</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line"></span><br><span class="line">String newString2 = new String(<span class="string">"faker大魔王"</span>);</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line">System.out.println(<span class="string">"init new String('faker大魔王') 2 info"</span>);</span><br><span class="line">System.out.println(GraphLayout.parseInstance(newString2).toPrintable());</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line"></span><br><span class="line">String newStringConcatString = new String(<span class="string">"faker"</span>)+<span class="string">"大魔王"</span>;</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line">System.out.println(<span class="string">"init String newStringConcatString = new String('faker')+'大魔王' info"</span>);</span><br><span class="line">System.out.println(GraphLayout.parseInstance(newStringConcatString).toPrintable());</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line"></span><br><span class="line">String newStringConcatString2 = new String(<span class="string">"faker"</span>)+new String(<span class="string">"大魔王"</span>);</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br><span class="line">System.out.println(<span class="string">"init String newStringConcatString2 = new String('faker')+new String('大魔王') info"</span>);</span><br><span class="line">System.out.println(GraphLayout.parseInstance(newStringConcatString2).toPrintable());</span><br><span class="line">System.out.println(<span class="string">"-------------------------------------------------------------------------------------------------------"</span>);</span><br></pre></td></tr></table></figure>
<h3 id="输出结果—jdk1-6"><a href="#输出结果—jdk1-6" class="headerlink" title="输出结果—jdk1.6"></a>输出结果—jdk1.6</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="literal">false</span></span><br><span class="line"><span class="literal">false</span></span><br><span class="line">use jol check ...</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">builderString info</span><br><span class="line">java.lang.String@1b17a8bdd object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">        7d5efcbe0         32 java.lang.String                                (object)</span><br><span class="line">        7d5efcc00         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init builderString.intern() info</span><br><span class="line">java.lang.String@60765a16d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">        77cfe3860         32 java.lang.String                                (object)</span><br><span class="line">        77cfe3880         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init String constStr = <span class="string">'faker大魔王'</span> info</span><br><span class="line">java.lang.String@60765a16d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">        77cfe3860         32 java.lang.String                                (object)</span><br><span class="line">        77cfe3880         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init new String(<span class="string">'faker大魔王'</span>) info</span><br><span class="line">java.lang.String@5ae80842d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">        77cfe3880         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line">        77cfe38a0 1498006336 (something <span class="keyword">else</span>) (somewhere <span class="keyword">else</span>)               (something <span class="keyword">else</span>)</span><br><span class="line">        7d647fbe0         32 java.lang.String                                (object)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init new String(<span class="string">'faker大魔王'</span>) 2 info</span><br><span class="line">java.lang.String@377653aed object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">        77cfe3880         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line">        77cfe38a0 1498033856 (something <span class="keyword">else</span>) (somewhere <span class="keyword">else</span>)               (something <span class="keyword">else</span>)</span><br><span class="line">        7d6486760         32 java.lang.String                                (object)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init String newStringConcatString = new String(<span class="string">'faker'</span>)+<span class="string">'大魔王'</span> info</span><br><span class="line">java.lang.String@396fe0f4d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">        7d648d358         32 java.lang.String                                (object)</span><br><span class="line">        7d648d378         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init String newStringConcatString2 = new String(<span class="string">'faker'</span>)+new String(<span class="string">'大魔王'</span>) info</span><br><span class="line">java.lang.String@3ed02b51d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">        7d6492950         32 java.lang.String                                (object)</span><br><span class="line">        7d6492970         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br></pre></td></tr></table></figure>
<h3 id="输出结果—jdk1-8"><a href="#输出结果—jdk1-8" class="headerlink" title="输出结果—jdk1.8"></a>输出结果—jdk1.8</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="literal">true</span></span><br><span class="line"><span class="literal">false</span></span><br><span class="line">use jol check ...</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">builderString info</span><br><span class="line">java.lang.String@1540e19dd object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">         d5ff44f8         24 java.lang.String                                (object)</span><br><span class="line">         d5ff4510         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init builderString.intern() info</span><br><span class="line">java.lang.String@1540e19dd object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">         d5ff44f8         24 java.lang.String                                (object)</span><br><span class="line">         d5ff4510         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init String constStr = <span class="string">'faker大魔王'</span> info</span><br><span class="line">java.lang.String@1540e19dd object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">         d5ff44f8         24 java.lang.String                                (object)</span><br><span class="line">         d5ff4510         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init new String(<span class="string">'faker大魔王'</span>) info</span><br><span class="line">java.lang.String@51521cc1d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">         d5ff4510         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line">         d5ff4530    8779704 (something <span class="keyword">else</span>) (somewhere <span class="keyword">else</span>)               (something <span class="keyword">else</span>)</span><br><span class="line">         d6853ce8         24 java.lang.String                                (object)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init new String(<span class="string">'faker大魔王'</span>) 2 info</span><br><span class="line">java.lang.String@1b4fb997d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">         d5ff4510         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line">         d5ff4530    8805504 (something <span class="keyword">else</span>) (somewhere <span class="keyword">else</span>)               (something <span class="keyword">else</span>)</span><br><span class="line">         d685a1b0         24 java.lang.String                                (object)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init String newStringConcatString = new String(<span class="string">'faker'</span>)+<span class="string">'大魔王'</span> info</span><br><span class="line">java.lang.String@deb6432d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">         d6860660         24 java.lang.String                                (object)</span><br><span class="line">         d6860678         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br><span class="line">init String newStringConcatString2 = new String(<span class="string">'faker'</span>)+new String(<span class="string">'大魔王'</span>) info</span><br><span class="line">java.lang.String@694f9431d object externals:</span><br><span class="line">          ADDRESS       SIZE TYPE             PATH                           VALUE</span><br><span class="line">         d6865708         24 java.lang.String                                (object)</span><br><span class="line">         d6865720         32 [C               .value                         [f, a, k, e, r, 大, 魔, 王]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-------------------------------------------------------------------------------------------------------</span><br></pre></td></tr></table></figure>
<h2 id="String、StringBuffer、StringBuilder"><a href="#String、StringBuffer、StringBuilder" class="headerlink" title="String、StringBuffer、StringBuilder"></a>String、StringBuffer、StringBuilder</h2><p>StringBuffer是StringBuilder线程安全版本的实现<br>所以以下着重讲StringBuilder为什么拼接会比String快，同时节约内存<br>以及为什么在字符串很短以及拼接次数少时，两者效率差距不大而到了次数多时，差距会变化很大</p>
<p>String中的拼接方法<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">   void getChars(char dst[], int dstBegin) &#123;</span><br><span class="line">       System.arraycopy(value, 0, dst, dstBegin, value.length);</span><br><span class="line">   &#125;</span><br><span class="line">   public String concat(String str) &#123;</span><br><span class="line">       int otherLen = str.length();</span><br><span class="line">       <span class="keyword">if</span> (otherLen == 0) &#123;</span><br><span class="line">           <span class="built_in">return</span> this;</span><br><span class="line">       &#125;</span><br><span class="line">       int len = value.length;</span><br><span class="line">       char buf[] = Arrays.copyOf(value, len + otherLen);</span><br><span class="line">       str.getChars(buf, len);</span><br><span class="line">       <span class="built_in">return</span> new String(buf, <span class="literal">true</span>);</span><br><span class="line">   &#125;</span><br><span class="line">/*</span><br><span class="line">   * Package private constructor <span class="built_in">which</span> shares value array <span class="keyword">for</span> speed.</span><br><span class="line">   * this constructor is always expected to be called with share==<span class="literal">true</span>.</span><br><span class="line">   * a separate constructor is needed because we already have a public</span><br><span class="line">   * String(char[]) constructor that makes a copy of the given char[].</span><br><span class="line">   */</span><br><span class="line">   String(char[] value, boolean share) &#123;</span><br><span class="line">       // assert share : <span class="string">"unshared not supported"</span>;</span><br><span class="line">       this.value = value;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>也就是每一次拼接的流程是创建一个长度刚好可以容纳这个拼接后的字符串的新数组，把两者字符复制进去<br>再来看看StringBuilder的实现<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">   public AbstractStringBuilder append(String str) &#123;</span><br><span class="line">       <span class="keyword">if</span> (str == null)</span><br><span class="line">           <span class="built_in">return</span> appendNull();</span><br><span class="line">       int len = str.length();</span><br><span class="line">       ensureCapacityInternal(count + len);</span><br><span class="line">       str.getChars(0, len, value, count);</span><br><span class="line">       count += len;</span><br><span class="line">       <span class="built_in">return</span> this;</span><br><span class="line">   &#125;</span><br><span class="line">private void ensureCapacityInternal(int minimumCapacity) &#123;</span><br><span class="line">       // overflow-conscious code</span><br><span class="line">       <span class="keyword">if</span> (minimumCapacity - value.length &gt; 0) &#123;</span><br><span class="line">           value = Arrays.copyOf(value,</span><br><span class="line">                   newCapacity(minimumCapacity));</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line">private int newCapacity(int minCapacity) &#123;</span><br><span class="line">       // overflow-conscious code</span><br><span class="line">       int newCapacity = (value.length &lt;&lt; 1) + 2;</span><br><span class="line">       <span class="keyword">if</span> (newCapacity - minCapacity &lt; 0) &#123;</span><br><span class="line">           newCapacity = minCapacity;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="built_in">return</span> (newCapacity &lt;= 0 || MAX_ARRAY_SIZE - newCapacity &lt; 0)</span><br><span class="line">           ? hugeCapacity(minCapacity)</span><br><span class="line">           : newCapacity;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p>
<p>StringBuilder和String一样在内部维护一个字符数组，但是StringBuilder在构造时会多预留16个位置<br>StringBuilder会调用父类AbstractStringBuilder的append方法<br>具体操作为如果当前数组长度不够存储，则将数组长度扩展为原来的两倍+2，如果扩展后还是不够，再直接一次性扩展到刚好满足<br>这意味着如果每次拼接的字符串相对当前字符串数组要小的多时，因为每次扩容是倍数增长，所以扩容次数相比String会减少<br>同时直接操作数组除了创建新数组没有创建别的对象，性能也更好(String的内部数组是final不可变，所以每次拼接都是创建新的String对象以及数组对象)<br>从这里也可以看出，如果每次拼接的字符串长度是指数增长，都要接近甚至更高于数组最新的长度，那么StringBuilder拼接的效率也会下降到和String差不多（当然这种场景是极少见的）</p>
<h3 id="请移步"><a href="#请移步" class="headerlink" title="请移步"></a>请移步</h3><p>个人主页: <a href="http://yangyitao.top" target="_blank" rel="noopener">yangyitao.top</a></p>

      
    </div>
    
    <div class="article-info article-info-index">
      
      
      

      
      <div class="clearfix"></div>
    </div>
      
    
  </div>
  
</article>







  
  
    <nav id="page-nav">
      <span class="page-number current">1</span><a class="page-number" href="/blog/page/2/">2</a><a class="page-number" href="/blog/page/3/">3</a><a class="extend next" rel="next" href="/blog/page/2/">Next &raquo;</a>
    </nav>
  
</div>
      <footer id="footer">
  <div class="outer">
    <div id="footer-info">
      <div class="footer-left">
        &copy; 2019 Eetal
      </div>
        <div class="footer-right">
          <a href="http://hexo.io/" target="_blank">Hexo</a>  Theme <a href="https://github.com/smackgg/hexo-theme-smackdown" target="_blank">Smackdown</a>
        </div>
    </div>
  </div>
</footer>
    </div>
    
  <link rel="stylesheet" href="/blog/fancybox/jquery.fancybox.css">


<script>
	var yiliaConfig = {
		fancybox: true,
		mathjax: true,
		animate: true,
		isHome: true,
		isPost: false,
		isArchive: false,
		isTag: false,
		isCategory: false,
		open_in_new: false
	}
</script>
<script src="/blog/js/main.js"></script>



<script type="text/x-mathjax-config">
MathJax.Hub.Config({
    tex2jax: {
        inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
        processEscapes: true,
        skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
    }
});

MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for(i=0; i < all.length; i += 1) {
        all[i].SourceElement().parentNode.className += ' has-jax';                 
    }       
});
</script>

<script src="//cdn.bootcss.com/mathjax/2.7.0/MathJax.js"></script>


  </div>
</body>
</html>